Merge "Hidden API usage Landroid/graphics/Canvas;->mBitmap:Landroid/graphics/Bitmap;"
diff --git a/Android.bp b/Android.bp
index 1012bb81..25e738c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -92,6 +92,7 @@
"core/java/android/app/IWallpaperManagerCallback.aidl",
"core/java/android/app/admin/IDeviceAdminService.aidl",
"core/java/android/app/admin/IDevicePolicyManager.aidl",
+ "core/java/android/app/admin/StartInstallingUpdateCallback.aidl",
"core/java/android/app/trust/IStrongAuthTracker.aidl",
"core/java/android/app/trust/ITrustManager.aidl",
"core/java/android/app/trust/ITrustListener.aidl",
@@ -254,6 +255,7 @@
":statsd_aidl",
"core/java/android/os/ISystemUpdateManager.aidl",
"core/java/android/os/IThermalEventListener.aidl",
+ "core/java/android/os/IThermalStatusListener.aidl",
"core/java/android/os/IThermalService.aidl",
"core/java/android/os/IUpdateLock.aidl",
"core/java/android/os/IUserManager.aidl",
@@ -294,6 +296,7 @@
"core/java/android/service/notification/IConditionListener.aidl",
"core/java/android/service/notification/IConditionProvider.aidl",
"core/java/android/service/settings/suggestions/ISuggestionService.aidl",
+ "core/java/android/service/sms/IFinancialSmsService.aidl",
"core/java/android/service/vr/IPersistentVrStateCallbacks.aidl",
"core/java/android/service/vr/IVrListener.aidl",
"core/java/android/service/vr/IVrManager.aidl",
@@ -334,8 +337,10 @@
"core/java/android/service/chooser/IChooserTargetResult.aidl",
"core/java/android/service/resolver/IResolverRankerService.aidl",
"core/java/android/service/resolver/IResolverRankerResult.aidl",
+ "core/java/android/service/textclassifier/IConversationActionsCallback.aidl",
"core/java/android/service/textclassifier/ITextClassificationCallback.aidl",
"core/java/android/service/textclassifier/ITextClassifierService.aidl",
+ "core/java/android/service/textclassifier/ITextLanguageCallback.aidl",
"core/java/android/service/textclassifier/ITextLinksCallback.aidl",
"core/java/android/service/textclassifier/ITextSelectionCallback.aidl",
"core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl",
@@ -344,6 +349,7 @@
"core/java/android/view/accessibility/IAccessibilityManagerClient.aidl",
"core/java/android/view/autofill/IAutoFillManager.aidl",
"core/java/android/view/autofill/IAutoFillManagerClient.aidl",
+ "core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
"core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
"core/java/android/view/intelligence/IIntelligenceManager.aidl",
"core/java/android/view/IApplicationToken.aidl",
@@ -455,7 +461,6 @@
"media/java/android/media/IMediaScannerListener.aidl",
"media/java/android/media/IMediaScannerService.aidl",
"media/java/android/media/IPlaybackConfigDispatcher.aidl",
- "media/java/android/media/ISessionTokensListener.aidl",
":libaudioclient_aidl",
"media/java/android/media/IRecordingConfigDispatcher.aidl",
"media/java/android/media/IRemoteDisplayCallback.aidl",
@@ -721,12 +726,15 @@
"android.hardware.wifi-V1.0-java-constants",
"android.hardware.radio-V1.0-java",
"android.hardware.radio-V1.3-java",
+ "android.hardware.radio-V1.4-java",
"android.hardware.usb.gadget-V1.0-java",
"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",
],
@@ -856,7 +864,7 @@
java_library {
name: "ext",
installable: true,
- sdk_version: "core_current",
+ no_framework_libs: true,
static_libs: [
"libphonenumber-platform",
"nist-sip",
@@ -1714,7 +1722,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/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ec3366c..e731138 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,8 +5,12 @@
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}
owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
+
+shell_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^packages/Shell/"
diff --git a/api/current.txt b/api/current.txt
old mode 100755
new mode 100644
index ff78d48..1b5b3ee
--- a/api/current.txt
+++ b/api/current.txt
@@ -159,9 +159,6 @@
field public static final java.lang.String WRITE_CONTACTS = "android.permission.WRITE_CONTACTS";
field public static final deprecated java.lang.String WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES";
- field public static final java.lang.String WRITE_MEDIA_AUDIO = "android.permission.WRITE_MEDIA_AUDIO";
- field public static final java.lang.String WRITE_MEDIA_IMAGES = "android.permission.WRITE_MEDIA_IMAGES";
- field public static final java.lang.String WRITE_MEDIA_VIDEO = "android.permission.WRITE_MEDIA_VIDEO";
field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
field public static final java.lang.String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS";
field public static final java.lang.String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS";
@@ -483,6 +480,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
@@ -1315,10 +1316,10 @@
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
+ field public static final int supportsMultipleDisplays = 16844183; // 0x1010597
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
@@ -1498,7 +1499,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
@@ -3804,6 +3807,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);
@@ -3881,6 +3885,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
@@ -5229,6 +5234,7 @@
ctor public Notification(android.os.Parcel);
method public android.app.Notification clone();
method public int describeContents();
+ method public boolean getAllowSystemGeneratedContextualActions();
method public android.app.PendingIntent getAppOverlayIntent();
method public int getBadgeIconType();
method public java.lang.String getChannelId();
@@ -5460,6 +5466,7 @@
method public android.app.Notification.Style getStyle();
method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
+ method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean);
method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setBadgeIconType(int);
@@ -6351,7 +6358,7 @@
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;
}
@@ -6509,6 +6516,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);
@@ -6538,6 +6546,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);
@@ -6598,6 +6607,7 @@
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, int);
+ method public void installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallUpdateCallback);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isAffiliatedUser();
@@ -6625,6 +6635,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);
@@ -6840,6 +6851,16 @@
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
}
+ public static abstract class DevicePolicyManager.InstallUpdateCallback {
+ ctor public DevicePolicyManager.InstallUpdateCallback();
+ method public void onInstallUpdateError(int, java.lang.String);
+ field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5
+ field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4
+ field public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2; // 0x2
+ field public static final int UPDATE_ERROR_UNKNOWN = 1; // 0x1
+ field public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3; // 0x3
+ }
+
public static abstract interface DevicePolicyManager.OnClearApplicationUserDataListener {
method public abstract void onApplicationUserDataCleared(java.lang.String, boolean);
}
@@ -7307,7 +7328,10 @@
method public android.content.Intent createRequestRoleIntent(java.lang.String);
method public boolean isRoleAvailable(java.lang.String);
method public boolean isRoleHeld(java.lang.String);
+ field public static final java.lang.String ROLE_BROWSER = "android.app.role.BROWSER";
field public static final java.lang.String ROLE_DIALER = "android.app.role.DIALER";
+ field public static final java.lang.String ROLE_GALLERY = "android.app.role.GALLERY";
+ field public static final java.lang.String ROLE_MUSIC = "android.app.role.MUSIC";
field public static final java.lang.String ROLE_SMS = "android.app.role.SMS";
}
@@ -9598,6 +9622,7 @@
method public abstract void unbindService(android.content.ServiceConnection);
method public void unregisterComponentCallbacks(android.content.ComponentCallbacks);
method public abstract void unregisterReceiver(android.content.BroadcastReceiver);
+ method public abstract void updateServiceGroup(android.content.ServiceConnection, int, int);
field public static final java.lang.String ACCESSIBILITY_SERVICE = "accessibility";
field public static final java.lang.String ACCOUNT_SERVICE = "account";
field public static final java.lang.String ACTIVITY_SERVICE = "activity";
@@ -9796,6 +9821,7 @@
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
+ method public void updateServiceGroup(android.content.ServiceConnection, int, int);
}
public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
@@ -10023,6 +10049,7 @@
field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW";
+ field public static final java.lang.String ACTION_DEFINE = "android.intent.action.DEFINE";
field public static final java.lang.String ACTION_DELETE = "android.intent.action.DELETE";
field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW";
field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK";
@@ -10113,6 +10140,7 @@
field public static final java.lang.String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET";
field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK";
+ field public static final java.lang.String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
@@ -11148,8 +11176,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;
@@ -11157,6 +11185,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;
}
@@ -11632,6 +11661,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 {
@@ -11694,6 +11724,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;
}
@@ -11810,6 +11841,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);
@@ -13919,6 +13972,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;
@@ -14671,6 +14725,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();
@@ -14804,6 +14859,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);
}
@@ -15023,6 +15079,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();
@@ -22623,7 +22680,7 @@
method public abstract void onLocationChanged(android.location.Location);
method public abstract void onProviderDisabled(java.lang.String);
method public abstract void onProviderEnabled(java.lang.String);
- method public abstract void onStatusChanged(java.lang.String, int, android.os.Bundle);
+ method public abstract deprecated void onStatusChanged(java.lang.String, int, android.os.Bundle);
}
public class LocationManager {
@@ -22635,7 +22692,7 @@
method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
method public void clearTestProviderEnabled(java.lang.String);
method public void clearTestProviderLocation(java.lang.String);
- method public void clearTestProviderStatus(java.lang.String);
+ method public deprecated void clearTestProviderStatus(java.lang.String);
method public java.util.List<java.lang.String> getAllProviders();
method public java.lang.String getBestProvider(android.location.Criteria, boolean);
method public java.lang.String getGnssHardwareModelName();
@@ -22672,7 +22729,7 @@
method public boolean sendExtraCommand(java.lang.String, java.lang.String, android.os.Bundle);
method public void setTestProviderEnabled(java.lang.String, boolean);
method public void setTestProviderLocation(java.lang.String, android.location.Location);
- method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
+ method public deprecated void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
@@ -22680,7 +22737,7 @@
field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
field public static final java.lang.String KEY_PROXIMITY_ENTERING = "entering";
- field public static final java.lang.String KEY_STATUS_CHANGED = "status";
+ field public static final deprecated java.lang.String KEY_STATUS_CHANGED = "status";
field public static final java.lang.String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
field public static final java.lang.String NETWORK_PROVIDER = "network";
field public static final java.lang.String PASSIVE_PROVIDER = "passive";
@@ -22699,9 +22756,9 @@
method public boolean supportsAltitude();
method public boolean supportsBearing();
method public boolean supportsSpeed();
- field public static final int AVAILABLE = 2; // 0x2
- field public static final int OUT_OF_SERVICE = 0; // 0x0
- field public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
+ field public static final deprecated int AVAILABLE = 2; // 0x2
+ field public static final deprecated int OUT_OF_SERVICE = 0; // 0x0
+ field public static final deprecated int TEMPORARILY_UNAVAILABLE = 1; // 0x1
}
public abstract interface OnNmeaMessageListener {
@@ -22930,7 +22987,7 @@
method public void adjustSuggestedStreamVolume(int, int, int);
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
- method public static int generateAudioSessionId();
+ method public int generateAudioSessionId();
method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
@@ -23439,6 +23496,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;
@@ -23446,11 +23504,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;
@@ -24570,6 +24630,7 @@
field public static final java.lang.String KEY_WIDTH = "width";
field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
+ field public static final java.lang.String MIMETYPE_AUDIO_AC4 = "audio/ac4";
field public static final java.lang.String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
field public static final java.lang.String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
field public static final java.lang.String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
@@ -29112,15 +29173,18 @@
method public android.net.wifi.WifiNetworkSuggestion buildNetworkSuggestion();
method public android.net.wifi.WifiNetworkConfigBuilder setBssid(android.net.MacAddress);
method public android.net.wifi.WifiNetworkConfigBuilder setBssidPattern(android.net.MacAddress, android.net.MacAddress);
- method public android.net.wifi.WifiNetworkConfigBuilder setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
method public android.net.wifi.WifiNetworkConfigBuilder setIsAppInteractionRequired();
+ method public android.net.wifi.WifiNetworkConfigBuilder setIsEnhancedOpen();
method public android.net.wifi.WifiNetworkConfigBuilder setIsHiddenSsid();
method public android.net.wifi.WifiNetworkConfigBuilder setIsMetered();
method public android.net.wifi.WifiNetworkConfigBuilder setIsUserInteractionRequired();
method public android.net.wifi.WifiNetworkConfigBuilder setPriority(int);
- method public android.net.wifi.WifiNetworkConfigBuilder setPskPassphrase(java.lang.String);
method public android.net.wifi.WifiNetworkConfigBuilder setSsid(java.lang.String);
method public android.net.wifi.WifiNetworkConfigBuilder setSsidPattern(android.os.PatternMatcher);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa2EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa2Passphrase(java.lang.String);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa3EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa3Passphrase(java.lang.String);
}
public final class WifiNetworkSuggestion implements android.os.Parcelable {
@@ -29457,7 +29521,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);
@@ -29502,6 +29569,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);
}
@@ -29514,6 +29585,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);
}
@@ -34630,14 +34709,14 @@
package android.preference {
- public class CheckBoxPreference extends android.preference.TwoStatePreference {
+ public deprecated class CheckBoxPreference extends android.preference.TwoStatePreference {
ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
ctor public CheckBoxPreference(android.content.Context);
}
- public abstract class DialogPreference extends android.preference.Preference implements android.content.DialogInterface.OnClickListener android.content.DialogInterface.OnDismissListener android.preference.PreferenceManager.OnActivityDestroyListener {
+ public abstract deprecated class DialogPreference extends android.preference.Preference implements android.content.DialogInterface.OnClickListener android.content.DialogInterface.OnDismissListener android.preference.PreferenceManager.OnActivityDestroyListener {
ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int);
ctor public DialogPreference(android.content.Context, android.util.AttributeSet);
@@ -34670,7 +34749,7 @@
method protected void showDialog(android.os.Bundle);
}
- public class EditTextPreference extends android.preference.DialogPreference {
+ public deprecated class EditTextPreference extends android.preference.DialogPreference {
ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int);
ctor public EditTextPreference(android.content.Context, android.util.AttributeSet);
@@ -34681,7 +34760,7 @@
method public void setText(java.lang.String);
}
- public class ListPreference extends android.preference.DialogPreference {
+ public deprecated class ListPreference extends android.preference.DialogPreference {
ctor public ListPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public ListPreference(android.content.Context, android.util.AttributeSet, int);
ctor public ListPreference(android.content.Context, android.util.AttributeSet);
@@ -34699,7 +34778,7 @@
method public void setValueIndex(int);
}
- public class MultiSelectListPreference extends android.preference.DialogPreference {
+ public deprecated class MultiSelectListPreference extends android.preference.DialogPreference {
ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int);
ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet);
@@ -34715,7 +34794,7 @@
method public void setValues(java.util.Set<java.lang.String>);
}
- public class Preference implements java.lang.Comparable {
+ public deprecated class Preference implements java.lang.Comparable {
ctor public Preference(android.content.Context, android.util.AttributeSet, int, int);
ctor public Preference(android.content.Context, android.util.AttributeSet, int);
ctor public Preference(android.content.Context, android.util.AttributeSet);
@@ -34812,21 +34891,21 @@
field public static final int DEFAULT_ORDER = 2147483647; // 0x7fffffff
}
- public static class Preference.BaseSavedState extends android.view.AbsSavedState {
+ public static deprecated class Preference.BaseSavedState extends android.view.AbsSavedState {
ctor public Preference.BaseSavedState(android.os.Parcel);
ctor public Preference.BaseSavedState(android.os.Parcelable);
field public static final android.os.Parcelable.Creator<android.preference.Preference.BaseSavedState> CREATOR;
}
- public static abstract interface Preference.OnPreferenceChangeListener {
+ public static abstract deprecated interface Preference.OnPreferenceChangeListener {
method public abstract boolean onPreferenceChange(android.preference.Preference, java.lang.Object);
}
- public static abstract interface Preference.OnPreferenceClickListener {
+ public static abstract deprecated interface Preference.OnPreferenceClickListener {
method public abstract boolean onPreferenceClick(android.preference.Preference);
}
- public abstract class PreferenceActivity extends android.app.ListActivity implements android.preference.PreferenceFragment.OnPreferenceStartFragmentCallback {
+ public abstract deprecated class PreferenceActivity extends android.app.ListActivity implements android.preference.PreferenceFragment.OnPreferenceStartFragmentCallback {
ctor public PreferenceActivity();
method public deprecated void addPreferencesFromIntent(android.content.Intent);
method public deprecated void addPreferencesFromResource(int);
@@ -34866,7 +34945,7 @@
field public static final long HEADER_ID_UNDEFINED = -1L; // 0xffffffffffffffffL
}
- public static final class PreferenceActivity.Header implements android.os.Parcelable {
+ public static final deprecated class PreferenceActivity.Header implements android.os.Parcelable {
ctor public PreferenceActivity.Header();
method public int describeContents();
method public java.lang.CharSequence getBreadCrumbShortTitle(android.content.res.Resources);
@@ -34892,14 +34971,14 @@
field public int titleRes;
}
- public class PreferenceCategory extends android.preference.PreferenceGroup {
+ public deprecated class PreferenceCategory extends android.preference.PreferenceGroup {
ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int, int);
ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int);
ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet);
ctor public PreferenceCategory(android.content.Context);
}
- public abstract interface PreferenceDataStore {
+ public abstract deprecated interface PreferenceDataStore {
method public default boolean getBoolean(java.lang.String, boolean);
method public default float getFloat(java.lang.String, float);
method public default int getInt(java.lang.String, int);
@@ -34929,7 +35008,7 @@
method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
}
- public abstract class PreferenceGroup extends android.preference.Preference {
+ public abstract deprecated class PreferenceGroup extends android.preference.Preference {
ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int, int);
ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int);
ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet);
@@ -34948,7 +35027,7 @@
method public void setOrderingAsAdded(boolean);
}
- public class PreferenceManager {
+ public deprecated class PreferenceManager {
method public android.preference.PreferenceScreen createPreferenceScreen(android.content.Context);
method public android.preference.Preference findPreference(java.lang.CharSequence);
method public static android.content.SharedPreferences getDefaultSharedPreferences(android.content.Context);
@@ -34970,19 +35049,19 @@
field public static final java.lang.String METADATA_KEY_PREFERENCES = "android.preference";
}
- public static abstract interface PreferenceManager.OnActivityDestroyListener {
+ public static abstract deprecated interface PreferenceManager.OnActivityDestroyListener {
method public abstract void onActivityDestroy();
}
- public static abstract interface PreferenceManager.OnActivityResultListener {
+ public static abstract deprecated interface PreferenceManager.OnActivityResultListener {
method public abstract boolean onActivityResult(int, int, android.content.Intent);
}
- public static abstract interface PreferenceManager.OnActivityStopListener {
+ public static abstract deprecated interface PreferenceManager.OnActivityStopListener {
method public abstract void onActivityStop();
}
- public final class PreferenceScreen extends android.preference.PreferenceGroup implements android.widget.AdapterView.OnItemClickListener android.content.DialogInterface.OnDismissListener {
+ public final deprecated class PreferenceScreen extends android.preference.PreferenceGroup implements android.widget.AdapterView.OnItemClickListener android.content.DialogInterface.OnDismissListener {
method public void bind(android.widget.ListView);
method public android.app.Dialog getDialog();
method public android.widget.ListAdapter getRootAdapter();
@@ -34991,7 +35070,7 @@
method public void onItemClick(android.widget.AdapterView, android.view.View, int, long);
}
- public class RingtonePreference extends android.preference.Preference implements android.preference.PreferenceManager.OnActivityResultListener {
+ public deprecated class RingtonePreference extends android.preference.Preference implements android.preference.PreferenceManager.OnActivityResultListener {
ctor public RingtonePreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public RingtonePreference(android.content.Context, android.util.AttributeSet, int);
ctor public RingtonePreference(android.content.Context, android.util.AttributeSet);
@@ -35008,7 +35087,7 @@
method public void setShowSilent(boolean);
}
- public class SwitchPreference extends android.preference.TwoStatePreference {
+ public deprecated class SwitchPreference extends android.preference.TwoStatePreference {
ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int);
ctor public SwitchPreference(android.content.Context, android.util.AttributeSet);
@@ -35021,7 +35100,7 @@
method public void setSwitchTextOn(int);
}
- public abstract class TwoStatePreference extends android.preference.Preference {
+ public abstract deprecated class TwoStatePreference extends android.preference.Preference {
ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int);
ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet);
@@ -37365,6 +37444,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);
@@ -37456,7 +37546,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);
}
@@ -40836,11 +40928,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();
@@ -42856,6 +42946,7 @@
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
+ field public static final java.lang.String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
field public static final java.lang.String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
@@ -42865,6 +42956,7 @@
field public static final java.lang.String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
field public static final java.lang.String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
+ field public static final java.lang.String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
field public static final java.lang.String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
field public static final java.lang.String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
field public static final java.lang.String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
@@ -43212,9 +43304,9 @@
public class MbmsGroupCallSession implements java.lang.AutoCloseable {
method public void close();
- method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback);
+ method public static android.telephony.MbmsGroupCallSession create(android.content.Context, int, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
- method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public android.telephony.mbms.GroupCall startGroupCall(long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, java.util.concurrent.Executor, android.telephony.mbms.GroupCallCallback);
}
public class MbmsStreamingSession implements java.lang.AutoCloseable {
@@ -43599,7 +43691,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);
@@ -44219,7 +44311,7 @@
public class GroupCall implements java.lang.AutoCloseable {
method public void close();
method public long getTmgi();
- method public void updateGroupCall(int[], int[]);
+ method public void updateGroupCall(java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3
field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6
@@ -44231,11 +44323,10 @@
field public static final int STATE_STOPPED = 1; // 0x1
}
- public class GroupCallCallback {
- ctor public GroupCallCallback();
- method public void onBroadcastSignalStrengthUpdated(int);
- method public void onError(int, java.lang.String);
- method public void onGroupCallStateChanged(int, int);
+ public abstract interface GroupCallCallback {
+ method public abstract void onBroadcastSignalStrengthUpdated(int);
+ method public abstract void onError(int, java.lang.String);
+ method public abstract void onGroupCallStateChanged(int, int);
field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
}
@@ -44275,6 +44366,11 @@
field public static final int ERROR_UNABLE_TO_READ_SIM = 206; // 0xce
}
+ public static class MbmsErrors.GroupCallErrors {
+ field public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502; // 0x1f6
+ field public static final int ERROR_UNABLE_TO_START_SERVICE = 501; // 0x1f5
+ }
+
public static class MbmsErrors.InitializationErrors {
field public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; // 0x66
field public static final int ERROR_DUPLICATE_INITIALIZE = 101; // 0x65
@@ -44287,12 +44383,11 @@
field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
}
- public class MbmsGroupCallSessionCallback {
- ctor public MbmsGroupCallSessionCallback();
- method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
- method public void onError(int, java.lang.String);
- method public void onMiddlewareReady();
- method public void onServiceInterfaceAvailable(java.lang.String, int);
+ public abstract interface MbmsGroupCallSessionCallback {
+ method public abstract void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
+ method public abstract void onError(int, java.lang.String);
+ method public abstract void onMiddlewareReady();
+ method public abstract void onServiceInterfaceAvailable(java.lang.String, int);
}
public class MbmsStreamingSessionCallback {
@@ -45593,9 +45688,14 @@
method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
}
- public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan {
+ public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan android.text.ParcelableSpan {
ctor public LineHeightSpan.Standard(int);
+ ctor public LineHeightSpan.Standard(android.os.Parcel);
method public void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
+ method public int describeContents();
+ method public int getHeight();
+ method public int getSpanTypeId();
+ method public void writeToParcel(android.os.Parcel, int);
}
public static abstract interface LineHeightSpan.WithDensity implements android.text.style.LineHeightSpan {
@@ -49082,7 +49182,7 @@
method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onProvideAutofillStructure(android.view.ViewStructure, int);
method public void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
- method public boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
+ method public void onProvideContentCaptureStructure(android.view.ViewStructure, int);
method public void onProvideStructure(android.view.ViewStructure);
method public void onProvideVirtualStructure(android.view.ViewStructure);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
@@ -49210,6 +49310,7 @@
method public void setLayoutDirection(int);
method public void setLayoutParams(android.view.ViewGroup.LayoutParams);
method public final void setLeft(int);
+ method public void setLeftTopRightBottom(int, int, int, int);
method public void setLongClickable(boolean);
method protected final void setMeasuredDimension(int, int);
method public void setMinimumHeight(int);
@@ -49298,6 +49399,8 @@
method public final boolean startDragAndDrop(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
method public boolean startNestedScroll(int);
method public void stopNestedScroll();
+ method public void transformMatrixToGlobal(android.graphics.Matrix);
+ method public void transformMatrixToLocal(android.graphics.Matrix);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
method public void unscheduleDrawable(android.graphics.drawable.Drawable);
method public final void updateDragShadow(android.view.View.DragShadowBuilder);
@@ -51890,6 +51993,7 @@
method public java.time.ZonedDateTime getTime();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Message> CREATOR;
+ field public static final android.app.Person PERSON_USER_LOCAL;
}
public static final class ConversationActions.Message.Builder {
@@ -52158,6 +52262,7 @@
field public static final int STATUS_LINKS_APPLIED = 0; // 0x0
field public static final int STATUS_NO_LINKS_APPLIED = 2; // 0x2
field public static final int STATUS_NO_LINKS_FOUND = 1; // 0x1
+ field public static final int STATUS_UNSUPPORTED_CHARACTER = 4; // 0x4
}
public static final class TextLinks.Builder {
@@ -54151,6 +54256,7 @@
ctor public ImageView(android.content.Context, android.util.AttributeSet);
ctor public ImageView(android.content.Context, android.util.AttributeSet, int);
ctor public ImageView(android.content.Context, android.util.AttributeSet, int, int);
+ method public void animateTransform(android.graphics.Matrix);
method public final void clearColorFilter();
method public boolean getAdjustViewBounds();
method public boolean getBaselineAlignBottom();
@@ -55448,6 +55554,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();
@@ -56234,6 +56341,7 @@
}
public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
+ ctor public InMemoryDexClassLoader(java.nio.ByteBuffer[], java.lang.String, java.lang.ClassLoader);
ctor public InMemoryDexClassLoader(java.nio.ByteBuffer[], java.lang.ClassLoader);
ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index f013b6d..b4c7336 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";
@@ -75,6 +76,7 @@
field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
+ field public static final java.lang.String GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS = "android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS";
field public static final java.lang.String GRANT_RUNTIME_PERMISSIONS = "android.permission.GRANT_RUNTIME_PERMISSIONS";
field public static final java.lang.String HARDWARE_TEST = "android.permission.HARDWARE_TEST";
field public static final java.lang.String HDMI_CEC = "android.permission.HDMI_CEC";
@@ -157,6 +159,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";
@@ -170,6 +173,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";
@@ -221,6 +225,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
}
@@ -440,10 +445,10 @@
}
public class KeyguardManager {
- method public void setPrivateNotificationsAllowed(boolean);
- method public boolean getPrivateNotificationsAllowed();
method public android.content.Intent createConfirmFactoryResetCredentialIntent(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+ method public boolean getPrivateNotificationsAllowed();
method public void requestDismissKeyguard(android.app.Activity, java.lang.CharSequence, android.app.KeyguardManager.KeyguardDismissCallback);
+ method public void setPrivateNotificationsAllowed(boolean);
}
public class Notification implements android.os.Parcelable {
@@ -552,6 +557,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);
@@ -586,6 +595,7 @@
method public boolean packageHasActiveAdmins(java.lang.String);
method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
method public void setDeviceProvisioningConfigApplied();
+ method public void setProfileOwnerCanAccessDeviceIdsForUser(android.content.ComponentName, android.os.UserHandle);
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
field public static final java.lang.String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
@@ -867,6 +877,7 @@
method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
+ method public void setRoleNamesFromController(java.util.List<java.lang.String>);
field public static final java.lang.String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
}
@@ -1052,6 +1063,7 @@
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";
@@ -1231,6 +1243,7 @@
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 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);
@@ -1817,9 +1830,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);
@@ -1836,6 +1859,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 {
@@ -3650,6 +3685,10 @@
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();
@@ -3661,6 +3700,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);
@@ -3935,6 +3975,7 @@
public final class ConfigUpdate {
field public static final java.lang.String ACTION_UPDATE_CARRIER_ID_DB = "android.os.action.UPDATE_CARRIER_ID_DB";
field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
+ field public static final java.lang.String ACTION_UPDATE_CONVERSATION_ACTIONS = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
field public static final java.lang.String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
field public static final java.lang.String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
@@ -4354,7 +4395,7 @@
package android.preference {
- public class PreferenceManager {
+ public deprecated class PreferenceManager {
method public boolean isStorageCredentialProtected();
method public void setStorageCredentialProtected();
}
@@ -4933,12 +4974,47 @@
package android.service.intelligence {
+ public final class FillCallback {
+ method public void onSuccess(android.service.intelligence.FillResponse);
+ }
+
+ public final class FillController {
+ method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>);
+ }
+
+ public final class FillRequest {
+ method public android.view.autofill.AutofillId getFocusedId();
+ method public android.service.intelligence.PresentationParams getPresentationParams();
+ method public android.service.intelligence.InteractionSessionId getSessionId();
+ }
+
+ public final class FillResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.intelligence.FillResponse> CREATOR;
+ }
+
+ public static class FillResponse.Builder {
+ ctor public FillResponse.Builder();
+ method public android.service.intelligence.FillResponse build();
+ method public android.service.intelligence.FillResponse.Builder setFillWindow(android.service.intelligence.FillWindow);
+ method public android.service.intelligence.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>);
+ }
+
+ public final class FillWindow {
+ ctor public FillWindow();
+ method public void destroy();
+ method public boolean update(android.service.intelligence.PresentationParams.Area, android.view.View, long);
+ 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";
}
@@ -4960,6 +5036,23 @@
field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionSessionId> CREATOR;
}
+ public abstract class PresentationParams {
+ method public int getFlags();
+ method public android.service.intelligence.PresentationParams.Area getFullArea();
+ method public android.service.intelligence.PresentationParams.Area getSuggestionArea();
+ field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
+ field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
+ field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
+ field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
+ field public static final int FLAG_HOST_IME = 16; // 0x10
+ field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
+ }
+
+ public static abstract class PresentationParams.Area {
+ method public android.graphics.Rect getBounds();
+ method public android.service.intelligence.PresentationParams.Area getSubArea(android.graphics.Rect);
+ }
+
public final class SnapshotData implements android.os.Parcelable {
method public int describeContents();
method public android.app.assist.AssistContent getAssistContent();
@@ -5018,14 +5111,20 @@
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);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+ method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public void onNotificationsSeen(java.util.List<java.lang.String>);
+ method public void onSuggestedReplySent(java.lang.String, java.lang.CharSequence, int);
method public final void unsnoozeNotification(java.lang.String);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+ field public static final int SOURCE_FROM_APP = 0; // 0x0
+ field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
}
public final class NotificationStats implements android.os.Parcelable {
@@ -5179,6 +5278,16 @@
}
+package android.service.sms {
+
+ public abstract class FinancialSmsService extends android.app.Service {
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract android.database.CursorWindow onGetSmsMessages(android.os.Bundle);
+ field public static final java.lang.String ACTION_FINANCIAL_SERVICE_INTENT = "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+ }
+
+}
+
package android.service.textclassifier {
public abstract class TextClassifierService extends android.app.Service {
@@ -5188,8 +5297,10 @@
method public abstract void onClassifyText(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextClassification.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextClassification>);
method public void onCreateTextClassificationSession(android.view.textclassifier.TextClassificationContext, android.view.textclassifier.TextClassificationSessionId);
method public void onDestroyTextClassificationSession(android.view.textclassifier.TextClassificationSessionId);
+ method public void onDetectLanguage(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextLanguage.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLanguage>);
method public abstract void onGenerateLinks(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextLinks.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>);
method public void onSelectionEvent(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.SelectionEvent);
+ method public void onSuggestConversationActions(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.ConversationActions.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.ConversationActions>);
method public abstract void onSuggestSelection(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextSelection.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.textclassifier.TextClassifierService";
}
@@ -5234,6 +5345,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 {
@@ -6251,6 +6371,45 @@
field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
+ public class ImsMmTelManager {
+ method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(android.content.Context, int);
+ method public int getVoWiFiModeSetting();
+ method public boolean isAdvancedCallingSettingEnabled();
+ method public boolean isAvailable(int, int);
+ method public boolean isCapable(int, int);
+ method public boolean isVoWiFiRoamingSettingEnabled();
+ method public boolean isVoWiFiSettingEnabled();
+ method public boolean isVtSettingEnabled();
+ method public void registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+ method public void registerMmTelCapabilityCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.CapabilityCallback);
+ method public void setAdvancedCallingSetting(boolean);
+ method public void setRttCapabilitySetting(boolean);
+ method public void setVoWiFiModeSetting(int);
+ method public void setVoWiFiNonPersistent(boolean, int);
+ method public void setVoWiFiRoamingModeSetting(int);
+ method public void setVoWiFiRoamingSetting(boolean);
+ method public void setVoWiFiSetting(boolean);
+ method public void setVtSetting(boolean);
+ method public void unregisterImsRegistrationCallback(android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+ method public void unregisterMmTelCapabilityCallback(android.telephony.ims.ImsMmTelManager.CapabilityCallback);
+ field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
+ field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
+ field public static final int WIFI_MODE_WIFI_PREFERRED = 2; // 0x2
+ }
+
+ public static class ImsMmTelManager.CapabilityCallback {
+ ctor public ImsMmTelManager.CapabilityCallback();
+ method public void onCapabilitiesStatusChanged(android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
+ }
+
+ public static class ImsMmTelManager.RegistrationCallback {
+ ctor public ImsMmTelManager.RegistrationCallback();
+ method public void onDeregistered(android.telephony.ims.ImsReasonInfo);
+ method public void onRegistered(int);
+ method public void onRegistering(int);
+ method public void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
+ }
+
public final class ImsReasonInfo implements android.os.Parcelable {
ctor public ImsReasonInfo(int, int, java.lang.String);
method public int describeContents();
@@ -6977,9 +7136,9 @@
method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
method public android.os.IBinder onBind(android.content.Intent);
- method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
method public void stopGroupCall(int, long);
- method public void updateGroupCall(int, long, int[], int[]);
+ method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
}
public class MbmsStreamingServiceBase extends android.os.Binder {
@@ -7471,7 +7630,7 @@
method public default void onMovedToDisplay(int, android.content.res.Configuration);
method public abstract void onOverScrolled(int, int, boolean, boolean);
method public default void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
- method public default boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
+ method public default void onProvideContentCaptureStructure(android.view.ViewStructure, int);
method public abstract void onProvideVirtualStructure(android.view.ViewStructure);
method public abstract void onScrollChanged(int, int, int, int);
method public abstract void onSizeChanged(int, int, int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 1c01cf1..c0f7ab6 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);
@@ -985,6 +990,7 @@
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
field public static final java.lang.String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
+ field public static final java.lang.String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS = "autofill_smart_suggestion_emulation_flags";
field public static final java.lang.String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode";
field public static final java.lang.String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold";
field public static final java.lang.String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
@@ -1154,13 +1160,19 @@
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);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+ method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public void onNotificationsSeen(java.util.List<java.lang.String>);
+ method public void onSuggestedReplySent(java.lang.String, java.lang.CharSequence, int);
method public final void unsnoozeNotification(java.lang.String);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+ field public static final int SOURCE_FROM_APP = 0; // 0x0
+ field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
}
public abstract class NotificationListenerService extends android.app.Service {
@@ -1318,9 +1330,9 @@
method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
method public android.os.IBinder onBind(android.content.Intent);
- method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
method public void stopGroupCall(int, long);
- method public void updateGroupCall(int, long, int[], int[]);
+ method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
}
public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index a826ec7..3723fce 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -17,6 +17,7 @@
package com.android.commands.bmgr;
import android.annotation.IntDef;
+import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupProgress;
@@ -29,6 +30,7 @@
import android.app.backup.ISelectBackupTransportCallback;
import android.app.backup.RestoreSet;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.os.Bundle;
@@ -37,8 +39,10 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArraySet;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -49,20 +53,34 @@
import java.util.Set;
import java.util.concurrent.CountDownLatch;
-public final class Bmgr {
- IBackupManager mBmgr;
- IRestoreSession mRestore;
+/**
+ * Adb shell command for {@link android.app.backup.IBackupManager}.
+ */
+public class Bmgr {
+ public static final String TAG = "Bmgr";
- static final String BMGR_NOT_RUNNING_ERR =
+ private final IBackupManager mBmgr;
+ private IRestoreSession mRestore;
+
+ private static final String BMGR_NOT_RUNNING_ERR =
"Error: Could not access the Backup Manager. Is the system running?";
- static final String TRANSPORT_NOT_RUNNING_ERR =
+ private static final String TRANSPORT_NOT_RUNNING_ERR =
"Error: Could not access the backup transport. Is the system running?";
- static final String PM_NOT_RUNNING_ERR =
+ private static final String PM_NOT_RUNNING_ERR =
"Error: Could not access the Package Manager. Is the system running?";
private String[] mArgs;
private int mNextArg;
+ @VisibleForTesting
+ Bmgr(IBackupManager bmgr) {
+ mBmgr = bmgr;
+ }
+
+ Bmgr() {
+ mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
+ }
+
public static void main(String[] args) {
try {
new Bmgr().run(args);
@@ -78,71 +96,73 @@
return;
}
- if (!isBmgrActive()) {
+ mArgs = args;
+ mNextArg = 0;
+ int userId = parseUserId();
+ String op = nextArg();
+ Slog.v(TAG, "Running " + op + " for user:" + userId);
+
+ if (!isBmgrActive(userId)) {
return;
}
- mArgs = args;
- String op = args[0];
- mNextArg = 1;
-
if ("enabled".equals(op)) {
- doEnabled();
+ doEnabled(userId);
return;
}
if ("enable".equals(op)) {
- doEnable();
+ doEnable(userId);
return;
}
if ("run".equals(op)) {
- doRun();
+ doRun(userId);
return;
}
if ("backup".equals(op)) {
- doBackup();
+ doBackup(userId);
return;
}
if ("init".equals(op)) {
- doInit();
+ doInit(userId);
return;
}
if ("list".equals(op)) {
- doList();
+ doList(userId);
return;
}
if ("restore".equals(op)) {
- doRestore();
+ doRestore(userId);
return;
}
if ("transport".equals(op)) {
- doTransport();
+ doTransport(userId);
return;
}
if ("wipe".equals(op)) {
- doWipe();
+ doWipe(userId);
return;
}
if ("fullbackup".equals(op)) {
- doFullTransportBackup();
+ doFullTransportBackup(userId);
return;
}
if ("backupnow".equals(op)) {
- doBackupNow();
+ doBackupNow(userId);
return;
}
if ("cancel".equals(op)) {
- doCancel();
+ doCancel(userId);
return;
}
@@ -155,15 +175,14 @@
showUsage();
}
- private boolean isBmgrActive() {
- mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
+ boolean isBmgrActive(@UserIdInt int userId) {
if (mBmgr == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return false;
}
try {
- if (!mBmgr.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
+ if (!mBmgr.isBackupServiceActive(userId)) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return false;
}
@@ -180,7 +199,7 @@
return enabled ? "enabled" : "disabled";
}
- private void doEnabled() {
+ private void doEnabled(@UserIdInt int userId) {
try {
boolean isEnabled = mBmgr.isBackupEnabled();
System.out.println("Backup Manager currently "
@@ -191,7 +210,7 @@
}
}
- private void doEnable() {
+ private void doEnable(@UserIdInt int userId) {
String arg = nextArg();
if (arg == null) {
showUsage();
@@ -211,7 +230,7 @@
}
}
- private void doRun() {
+ void doRun(@UserIdInt int userId) {
try {
mBmgr.backupNow();
} catch (RemoteException e) {
@@ -220,7 +239,7 @@
}
}
- private void doBackup() {
+ private void doBackup(@UserIdInt int userId) {
String pkg = nextArg();
if (pkg == null) {
showUsage();
@@ -235,7 +254,7 @@
}
}
- private void doFullTransportBackup() {
+ private void doFullTransportBackup(@UserIdInt int userId) {
System.out.println("Performing full transport backup");
String pkg;
@@ -354,8 +373,8 @@
}
}
- private void backupNowAllPackages(boolean nonIncrementalBackup, @Monitor int monitorState) {
- int userId = UserHandle.USER_SYSTEM;
+ private void backupNowAllPackages(@UserIdInt int userId, boolean nonIncrementalBackup,
+ @Monitor int monitorState) {
IPackageManager mPm =
IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
if (mPm == null) {
@@ -379,11 +398,13 @@
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
- backupNowPackages(Arrays.asList(filteredPackages), nonIncrementalBackup, monitorState);
+ backupNowPackages(userId, Arrays.asList(filteredPackages), nonIncrementalBackup,
+ monitorState);
}
}
private void backupNowPackages(
+ @UserIdInt int userId,
List<String> packages, boolean nonIncrementalBackup, @Monitor int monitorState) {
int flags = 0;
if (nonIncrementalBackup) {
@@ -412,7 +433,7 @@
}
}
- private void doBackupNow() {
+ private void doBackupNow(@UserIdInt int userId) {
String pkg;
boolean backupAll = false;
boolean nonIncrementalBackup = false;
@@ -439,20 +460,20 @@
if (allPkgs.size() == 0) {
System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
"incremental backup for all packages.");
- backupNowAllPackages(nonIncrementalBackup, monitor);
+ backupNowAllPackages(userId, nonIncrementalBackup, monitor);
} else {
System.err.println("Provide only '--all' flag or list of packages.");
}
} else if (allPkgs.size() > 0) {
System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
"incremental backup for " + allPkgs.size() +" requested packages.");
- backupNowPackages(allPkgs, nonIncrementalBackup, monitor);
+ backupNowPackages(userId, allPkgs, nonIncrementalBackup, monitor);
} else {
System.err.println("Provide '--all' flag or list of packages.");
}
}
- private void doCancel() {
+ private void doCancel(@UserIdInt int userId) {
String arg = nextArg();
if ("backups".equals(arg)) {
try {
@@ -467,7 +488,7 @@
System.err.println("Unknown command.");
}
- private void doTransport() {
+ private void doTransport(@UserIdInt int userId) {
try {
String which = nextArg();
if (which == null) {
@@ -531,7 +552,7 @@
}
}
- private void doWipe() {
+ private void doWipe(@UserIdInt int userId) {
String transport = nextArg();
if (transport == null) {
showUsage();
@@ -563,7 +584,7 @@
}
}
- private void doInit() {
+ private void doInit(@UserIdInt int userId) {
ArraySet<String> transports = new ArraySet<>();
String transport;
while ((transport = nextArg()) != null) {
@@ -586,7 +607,7 @@
}
}
- private void doList() {
+ private void doList(@UserIdInt int userId) {
String arg = nextArg(); // sets, transports, packages set#
if ("transports".equals(arg)) {
doListTransports();
@@ -603,8 +624,6 @@
if ("sets".equals(arg)) {
doListRestoreSets();
- } else if ("transports".equals(arg)) {
- doListTransports();
}
mRestore.endRestoreSession();
@@ -717,7 +736,7 @@
}
}
- private void doRestore() {
+ private void doRestore(@UserIdInt int userId) {
String arg = nextArg();
if (arg == null) {
showUsage();
@@ -830,8 +849,18 @@
return arg;
}
+ private int parseUserId() {
+ String arg = nextArg();
+ if ("--user".equals(arg)) {
+ return UserHandle.parseUserArg(nextArg());
+ } else {
+ mNextArg--;
+ return UserHandle.USER_SYSTEM;
+ }
+ }
+
private static void showUsage() {
- System.err.println("usage: bmgr [backup|restore|list|transport|run]");
+ System.err.println("usage: bmgr [--user <userId>] [backup|restore|list|transport|run]");
System.err.println(" bmgr backup PACKAGE");
System.err.println(" bmgr enable BOOL");
System.err.println(" bmgr enabled");
@@ -847,6 +876,10 @@
System.err.println(" bmgr cancel backups");
System.err.println(" bmgr init TRANSPORT...");
System.err.println("");
+ System.err.println("The '--user' option specifies the user on which the operation is run.");
+ System.err.println("It must be the first argument before the operation.");
+ System.err.println("The default value is 0 which is the system user.");
+ System.err.println("");
System.err.println("The 'backup' command schedules a backup pass for the named package.");
System.err.println("Note that the backup pass will effectively be a no-op if the package");
System.err.println("does not actually have changed data to store.");
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
new file mode 100644
index 0000000..31bd612
--- /dev/null
+++ b/cmds/bootanimation/Android.bp
@@ -0,0 +1,90 @@
+cc_defaults {
+ name: "bootanimation_defaults",
+
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DEGL_EGLEXT_PROTOTYPES",
+
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ shared_libs: [
+ "libandroidfw",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+}
+
+// bootanimation executable
+// =========================================================
+
+cc_binary {
+ name: "bootanimation",
+ defaults: ["bootanimation_defaults"],
+
+ shared_libs: [
+ "libOpenSLES",
+ "libbootanimation",
+ ],
+
+ srcs: [
+ "BootAnimationUtil.cpp",
+
+ "bootanimation_main.cpp",
+ "audioplay.cpp",
+ ],
+
+ product_variables: {
+ product_is_iot: {
+ shared_libs: [
+ "libandroidthings",
+ "libandroidthings_protos",
+ "libchrome",
+ "libprotobuf-cpp-lite",
+ ],
+ static_libs: ["libjsoncpp"],
+ srcs: [
+ "iot/iotbootanimation_main.cpp",
+ "iot/BootAction.cpp",
+ "iot/BootParameters.cpp",
+ ],
+ exclude_srcs: [
+ "bootanimation_main.cpp",
+ "audioplay.cpp",
+ ],
+ },
+ },
+
+ init_rc: ["bootanim.rc"],
+}
+
+// libbootanimation
+// ===========================================================
+
+cc_library_shared {
+ name: "libbootanimation",
+ defaults: ["bootanimation_defaults"],
+
+ srcs: ["BootAnimation.cpp"],
+
+ shared_libs: [
+ "libui",
+ "libhwui",
+ "libEGL",
+ "libGLESv1_CM",
+ "libgui",
+ "libtinyalsa",
+ ],
+
+ product_variables: {
+ product_is_iot: {
+ init_rc: ["iot/bootanim_iot.rc"],
+ },
+ },
+}
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
deleted file mode 100644
index 6943dab..0000000
--- a/cmds/bootanimation/Android.mk
+++ /dev/null
@@ -1,103 +0,0 @@
-bootanimation_CommonCFlags = -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-bootanimation_CommonCFlags += -Wall -Werror -Wunused -Wunreachable-code
-
-
-# bootanimation executable
-# =========================================================
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
-
-LOCAL_SHARED_LIBRARIES := \
- libOpenSLES \
- libandroidfw \
- libbase \
- libbinder \
- libbootanimation \
- libcutils \
- liblog \
- libutils \
-
-LOCAL_SRC_FILES:= \
- BootAnimationUtil.cpp \
-
-ifeq ($(PRODUCT_IOT),true)
-
-LOCAL_SHARED_LIBRARIES += \
- libandroidthings \
- libandroidthings_protos \
- libchrome \
- libprotobuf-cpp-lite \
-
-LOCAL_STATIC_LIBRARIES += \
- libjsoncpp
-
-LOCAL_SRC_FILES += \
- iot/iotbootanimation_main.cpp \
- iot/BootAction.cpp \
- iot/BootParameters.cpp \
-
-else
-
-LOCAL_SRC_FILES += \
- bootanimation_main.cpp \
- audioplay.cpp \
-
-endif # PRODUCT_IOT
-
-LOCAL_MODULE:= bootanimation
-
-LOCAL_INIT_RC := bootanim.rc
-
-ifdef TARGET_32_BIT_SURFACEFLINGER
-LOCAL_32_BIT_ONLY := true
-endif
-
-include $(BUILD_EXECUTABLE)
-
-
-# libbootanimation
-# ===========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbootanimation
-LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
-
-LOCAL_SRC_FILES:= \
- BootAnimation.cpp
-
-LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
-
-LOCAL_C_INCLUDES += \
- external/tinyalsa/include \
- frameworks/wilhelm/include
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog \
- libandroidfw \
- libutils \
- libbinder \
- libui \
- libhwui \
- libEGL \
- libGLESv1_CM \
- libgui \
- libtinyalsa \
- libbase
-
-ifeq ($(PRODUCT_IOT),true)
-
-LOCAL_INIT_RC := iot/bootanim_iot.rc
-
-endif # PRODUCT_IOT
-
-ifdef TARGET_32_BIT_SURFACEFLINGER
-LOCAL_32_BIT_ONLY := true
-endif
-
-include ${BUILD_SHARED_LIBRARY}
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/cmds/bootanimation/iot/Android.bp b/cmds/bootanimation/iot/Android.bp
new file mode 100644
index 0000000..1f248ad
--- /dev/null
+++ b/cmds/bootanimation/iot/Android.bp
@@ -0,0 +1,49 @@
+// 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.
+//
+
+// libbootanimation_iot_test
+// ===========================================================
+cc_test {
+ name: "libbootanimation_iot_test",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ shared_libs: [
+ "libandroidthings",
+ "libandroidthings_protos",
+ "libbase",
+ "libchrome",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ ],
+
+ static_libs: ["libjsoncpp"],
+
+ srcs: [
+ "BootParameters.cpp",
+ "BootParameters_test.cpp",
+ ],
+
+ enabled: false,
+ product_variables: {
+ product_is_iot: {
+ enabled: true,
+ },
+ },
+}
diff --git a/cmds/bootanimation/iot/Android.mk b/cmds/bootanimation/iot/Android.mk
deleted file mode 100644
index 3d288e4..0000000
--- a/cmds/bootanimation/iot/Android.mk
+++ /dev/null
@@ -1,43 +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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-ifeq ($(PRODUCT_IOT),true)
-
-# libbootanimation_iot_test
-# ===========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbootanimation_iot_test
-LOCAL_CFLAGS := -Wall -Werror -Wunused -Wunreachable-code
-
-LOCAL_SHARED_LIBRARIES := \
- libandroidthings \
- libandroidthings_protos \
- libbase \
- libchrome \
- liblog \
- libprotobuf-cpp-lite \
-
-LOCAL_STATIC_LIBRARIES += \
- libjsoncpp
-
-LOCAL_SRC_FILES := \
- BootParameters.cpp \
- BootParameters_test.cpp \
-
-include $(BUILD_NATIVE_TEST)
-
-endif # PRODUCT_IOT
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 376b13c..6c6797a 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -48,6 +48,8 @@
private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
private static final String COMMAND_FORCE_NETWORK_LOGS = "force-network-logs";
private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
+ private static final String COMMAND_GRANT_PO_DEVICE_ID_ACCESS =
+ "grant-profile-owner-device-ids-access";
private IDevicePolicyManager mDevicePolicyManager;
private int mUserId = UserHandle.USER_SYSTEM;
@@ -89,7 +91,10 @@
"the DPC and triggers DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n" +
"\n" +
"dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
- "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.");
+ "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed."
+ + "\n"
+ + "usage: dpm " + COMMAND_GRANT_PO_DEVICE_ID_ACCESS + ": "
+ + "[ --user <USER_ID> | current ] <COMPONENT>\n");
}
@Override
@@ -124,6 +129,9 @@
case COMMAND_FORCE_SECURITY_LOGS:
runForceSecurityLogs();
break;
+ case COMMAND_GRANT_PO_DEVICE_ID_ACCESS:
+ runGrantProfileOwnerDeviceIdsAccess();
+ break;
default:
throw new IllegalArgumentException ("unknown command '" + command + "'");
}
@@ -242,6 +250,13 @@
System.out.println("Success");
}
+
+ private void runGrantProfileOwnerDeviceIdsAccess() throws RemoteException {
+ parseArgs(/*canHaveName=*/ false);
+ mDevicePolicyManager.grantDeviceIdsAccessToProfileOwner(mComponent, mUserId);
+ System.out.println("Success");
+ }
+
private ComponentName parseComponentName(String component) {
ComponentName cn = ComponentName.unflattenFromString(component);
if (cn == null) {
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 5a6c813..d7922bc 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -18,7 +18,7 @@
tidy: true,
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
"libidmap2/BinaryStreamVisitor.cpp",
@@ -64,7 +64,7 @@
tidy: true,
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
"tests/BinaryStreamVisitorTests.cpp",
@@ -118,7 +118,7 @@
tidy: true,
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
"idmap2/Create.cpp",
@@ -165,7 +165,7 @@
],
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
":idmap2_aidl",
@@ -181,6 +181,7 @@
"libutils",
"libziparchive",
],
+ init_rc: ["idmap2d/idmap2d.rc"],
}
filegroup {
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 1191e6a..020c443 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -48,8 +48,6 @@
using android::Res_value;
using android::ResStringPool;
using android::ResTable_config;
-using android::String16;
-using android::String8;
using android::StringPiece16;
using android::base::StringPrintf;
using android::idmap2::CommandLineOptions;
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/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3ee0a06..c8405a2 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.
@@ -205,6 +208,7 @@
DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042;
BatteryLevel battery_level = 10043;
+ BuildInformation build_information = 10044;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -1334,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.
@@ -1349,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;
}
/**
@@ -2421,8 +2428,8 @@
*/
message CpuTimePerUid {
optional int32 uid = 1 [(is_uid) = true];
- optional uint64 user_time_millis = 2;
- optional uint64 sys_time_millis = 3;
+ optional uint64 user_time_micros = 2;
+ optional uint64 sys_time_micros = 3;
}
/**
@@ -3331,6 +3338,40 @@
}
/**
+ * Pulls information about the device's build.
+ */
+message BuildInformation {
+ // Build.FINGERPRINT. A string that uniquely identifies this build. Do not parse.
+ // E.g. may be composed of the brand, product, device, release, id, incremental, type, and tags.
+ optional string fingerprint = 1;
+
+ // Build.BRAND. The consumer-visible brand with which the product/hardware will be associated.
+ optional string brand = 2;
+
+ // Build.PRODUCT. The name of the overall product.
+ optional string product = 3;
+
+ // Build.DEVICE. The name of the industrial design.
+ optional string device = 4;
+
+ // Build.VERSION.RELEASE. The user-visible version string. E.g., "1.0" or "3.4b5" or "bananas".
+ optional string version_release = 5;
+
+ // Build.ID. E.g. a label like "M4-rc20".
+ optional string id = 6;
+
+ // Build.VERSION.INCREMENTAL. The internal value used by the underlying source control to
+ // represent this build.
+ optional string version_incremental = 7;
+
+ // Build.TYPE. The type of build, like "user" or "eng".
+ optional string type = 8;
+
+ // Build.TAGS. Comma-separated tags describing the build, like "unsigned,debug".
+ optional string tags = 9;
+}
+
+/**
* Pulls on-device BatteryStats power use calculations for the overall device.
*/
message DeviceCalculatedPowerUse {
@@ -3389,3 +3430,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 ab635a0..f9b7982 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,164 +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},
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)}},
};
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.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 343709a..3157037 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -113,7 +113,7 @@
// Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
// drops the metrics data in memory.
- static const size_t kMaxMetricsBytesPerConfig = 256 * 1024;
+ static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
// Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
// data subscriber that it's time to call getData.
@@ -130,7 +130,7 @@
static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC;
/* Min period between two checks of byte size per config key in nanoseconds. */
- static const int64_t kMinByteSizeCheckPeriodNs = 10 * NS_PER_SEC;
+ static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC;
// Maximum age (30 days) that files on disk can exist in seconds.
static const int kMaxAgeSecond = 60 * 60 * 24 * 30;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 625294c..b9732a5 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;
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/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 6384757..90dfa87 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;
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/config/boot-image-profile.txt b/config/boot-image-profile.txt
index e5764f0..c2e441b 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -12274,7 +12274,7 @@
HPLorg/ccil/cowan/tagsoup/AttributesImpl;->setAttribute(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
HPLorg/ccil/cowan/tagsoup/Element;->anonymize()V
HPLorg/ccil/cowan/tagsoup/Element;->preclose()V
-HPLorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V
+# HPLorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V b/76145463
HPLorg/json/JSONArray;->optDouble(I)D
HPLorg/json/JSONArray;->optDouble(ID)D
HPLorg/json/JSONStringer;->value(J)Lorg/json/JSONStringer;
@@ -15623,7 +15623,7 @@
HSPLandroid/app/admin/IDevicePolicyManager$Stub$Proxy;->isLockTaskPermitted(Ljava/lang/String;)Z
HSPLandroid/app/admin/IDevicePolicyManager$Stub$Proxy;->isProvisioningAllowed(Ljava/lang/String;Ljava/lang/String;)Z
HSPLandroid/app/admin/IDevicePolicyManager$Stub;-><init>()V
-HSPLandroid/app/admin/IDevicePolicyManager$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
+# HSPLandroid/app/admin/IDevicePolicyManager$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z b/76145463
HSPLandroid/app/admin/IDevicePolicyManager;->addCrossProfileIntentFilter(Landroid/content/ComponentName;Landroid/content/IntentFilter;I)V
HSPLandroid/app/admin/IDevicePolicyManager;->addCrossProfileWidgetProvider(Landroid/content/ComponentName;Ljava/lang/String;)Z
HSPLandroid/app/admin/IDevicePolicyManager;->addOverrideApn(Landroid/content/ComponentName;Landroid/telephony/data/ApnSetting;)I
@@ -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
@@ -25254,7 +25255,7 @@
HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->parseFromInfo(Landroid/media/MediaFormat;)V
HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->parseWidthHeightRanges(Ljava/lang/Object;)Landroid/util/Pair;
HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->supports(Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Number;)Z
-HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->updateLimits()V
+# HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->updateLimits()V b/76145463
HSPLandroid/media/MediaCodecInfo;-><init>(Ljava/lang/String;Z[Landroid/media/MediaCodecInfo$CodecCapabilities;)V
HSPLandroid/media/MediaCodecInfo;->access$200()Landroid/util/Range;
HSPLandroid/media/MediaCodecInfo;->access$300()Landroid/util/Range;
@@ -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
@@ -37845,7 +37846,7 @@
HSPLandroid/widget/ViewSwitcher;->getNextView()Landroid/view/View;
HSPLandroid/widget/WrapperListAdapter;->getWrappedAdapter()Landroid/widget/ListAdapter;
HSPLcom/android/i18n/phonenumbers/AlternateFormatsCountryCodeSet;->getCountryCodeSet()Ljava/util/Set;
-HSPLcom/android/i18n/phonenumbers/CountryCodeToRegionCodeMap;->getCountryCodeToRegionCodeMap()Ljava/util/Map;
+# HSPLcom/android/i18n/phonenumbers/CountryCodeToRegionCodeMap;->getCountryCodeToRegionCodeMap()Ljava/util/Map; b/76145463
HSPLcom/android/i18n/phonenumbers/MetadataLoader;->loadMetadata(Ljava/lang/String;)Ljava/io/InputStream;
HSPLcom/android/i18n/phonenumbers/MetadataManager$1;-><init>()V
HSPLcom/android/i18n/phonenumbers/MetadataManager$1;->loadMetadata(Ljava/lang/String;)Ljava/io/InputStream;
@@ -37999,7 +38000,7 @@
HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setCountryCodeSource(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setNationalNumber(J)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setRawInput(Ljava/lang/String;)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-HSPLcom/android/i18n/phonenumbers/ShortNumbersRegionCodeSet;->getRegionCodeSet()Ljava/util/Set;
+# HSPLcom/android/i18n/phonenumbers/ShortNumbersRegionCodeSet;->getRegionCodeSet()Ljava/util/Set; b/76145463
HSPLcom/android/i18n/phonenumbers/internal/MatcherApi;->matchNationalNumber(Ljava/lang/CharSequence;Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;Z)Z
HSPLcom/android/i18n/phonenumbers/internal/RegexBasedMatcher;->matchNationalNumber(Ljava/lang/CharSequence;Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;Z)Z
HSPLcom/android/i18n/phonenumbers/internal/RegexCache$LRUCache$1;->removeEldestEntry(Ljava/util/Map$Entry;)Z
@@ -38978,7 +38979,7 @@
HSPLcom/android/internal/os/BatteryStatsImpl$Uid;->updateUidProcessStateLocked(I)V
HSPLcom/android/internal/os/BatteryStatsImpl$Uid;->writeJobCompletionsToParcelLocked(Landroid/os/Parcel;)V
HSPLcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;->getUserIds()[I
-HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Landroid/os/Parcel;)V
+# HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Landroid/os/Parcel;)V b/76145463
HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Ljava/io/File;Landroid/os/Handler;Lcom/android/internal/os/BatteryStatsImpl$PlatformIdleStateCallback;Lcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;)V
HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Ljava/io/File;Landroid/os/Handler;Lcom/android/internal/os/BatteryStatsImpl$PlatformIdleStateCallback;Lcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;)V
HSPLcom/android/internal/os/BatteryStatsImpl;->addHistoryBufferLocked(JBLandroid/os/BatteryStats$HistoryItem;)V
@@ -41154,7 +41155,7 @@
HSPLcom/android/internal/telephony/TimeServiceHelper;->setListener(Lcom/android/internal/telephony/TimeServiceHelper$Listener;)V
HSPLcom/android/internal/telephony/TimeZoneLookupHelper$CountryResult;->toString()Ljava/lang/String;
HSPLcom/android/internal/telephony/TimeZoneLookupHelper$OffsetResult;->toString()Ljava/lang/String;
-HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->getCountryTimeZones(Ljava/lang/String;)Llibcore/util/CountryTimeZones;
+HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->getCountryTimeZones(Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->lookupByCountry(Ljava/lang/String;J)Lcom/android/internal/telephony/TimeZoneLookupHelper$CountryResult;
HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->lookupByNitzCountry(Lcom/android/internal/telephony/NitzData;Ljava/lang/String;)Lcom/android/internal/telephony/TimeZoneLookupHelper$OffsetResult;
HSPLcom/android/internal/telephony/UiccSmsController;->disableCellBroadcastRangeForSubscriber(IIII)Z
@@ -51930,7 +51931,46 @@
HSPLlibcore/reflect/Types;->getTypeArray(Llibcore/reflect/ListOfTypes;Z)[Ljava/lang/reflect/Type;
HSPLlibcore/reflect/WildcardTypeImpl;->getLowerBounds()[Ljava/lang/reflect/Type;
HSPLlibcore/reflect/WildcardTypeImpl;->getUpperBounds()[Ljava/lang/reflect/Type;
-HSPLlibcore/util/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/CountryTimeZones;->createValidated(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
+HSPLlibcore/timezone/CountryTimeZones;->getDefaultTimeZone()Landroid/icu/util/TimeZone;
+HSPLlibcore/timezone/CountryTimeZones;->getIcuTimeZones()Ljava/util/List;
+HSPLlibcore/timezone/CountryTimeZones;->isDefaultOkForCountryTimeZoneDetection(J)Z
+HSPLlibcore/timezone/CountryTimeZones;->isForCountryCode(Ljava/lang/String;)Z
+HSPLlibcore/timezone/CountryTimeZones;->lookupByOffsetWithBias(IZJLandroid/icu/util/TimeZone;)Llibcore/timezone/CountryTimeZones$OffsetResult;
+HSPLlibcore/timezone/TimeZoneDataFiles;->generateIcuDataPath()Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneDataFiles;->getTimeZoneFilePaths(Ljava/lang/String;)[Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneFinder$ReaderSupplier;->forFile(Ljava/lang/String;Ljava/nio/charset/Charset;)Llibcore/timezone/TimeZoneFinder$ReaderSupplier;
+HSPLlibcore/timezone/TimeZoneFinder$ReaderSupplier;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;->processCountryZones(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Z
+HSPLlibcore/timezone/TimeZoneFinder$TimeZonesProcessor;->processHeader(Ljava/lang/String;)Z
+HSPLlibcore/timezone/TimeZoneFinder;->checkOnEndTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;)V
+HSPLlibcore/timezone/TimeZoneFinder;->consumeText(Lorg/xmlpull/v1/XmlPullParser;)Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneFinder;->createInstanceWithFallback([Ljava/lang/String;)Llibcore/timezone/TimeZoneFinder;
+HSPLlibcore/timezone/TimeZoneFinder;->findStartTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Z)Z
+HSPLlibcore/timezone/TimeZoneFinder;->getInstance()Llibcore/timezone/TimeZoneFinder;
+HSPLlibcore/timezone/TimeZoneFinder;->lookupCountryTimeZones(Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
+HSPLlibcore/timezone/TimeZoneFinder;->parseBooleanAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean;
+HSPLlibcore/timezone/TimeZoneFinder;->parseLongAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
+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
@@ -51938,12 +51978,6 @@
HSPLlibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
HSPLlibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
HSPLlibcore/util/CollectionUtils;->removeDuplicates(Ljava/util/List;Ljava/util/Comparator;)V
-HSPLlibcore/util/CountryTimeZones;->createValidated(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Llibcore/util/CountryTimeZones;
-HSPLlibcore/util/CountryTimeZones;->getDefaultTimeZone()Landroid/icu/util/TimeZone;
-HSPLlibcore/util/CountryTimeZones;->getIcuTimeZones()Ljava/util/List;
-HSPLlibcore/util/CountryTimeZones;->isDefaultOkForCountryTimeZoneDetection(J)Z
-HSPLlibcore/util/CountryTimeZones;->isForCountryCode(Ljava/lang/String;)Z
-HSPLlibcore/util/CountryTimeZones;->lookupByOffsetWithBias(IZJLandroid/icu/util/TimeZone;)Llibcore/util/CountryTimeZones$OffsetResult;
HSPLlibcore/util/HexEncoding;->encode([BII)[C
HSPLlibcore/util/NativeAllocationRegistry$CleanerRunner;->run()V
HSPLlibcore/util/NativeAllocationRegistry$CleanerThunk;->run()V
@@ -51951,23 +51985,6 @@
HSPLlibcore/util/NativeAllocationRegistry;->registerNativeAllocation(Ljava/lang/Object;J)Ljava/lang/Runnable;
HSPLlibcore/util/SneakyThrow;->sneakyThrow(Ljava/lang/Throwable;)V
HSPLlibcore/util/SneakyThrow;->sneakyThrow_(Ljava/lang/Throwable;)V
-HSPLlibcore/util/TimeZoneDataFiles;->generateIcuDataPath()Ljava/lang/String;
-HSPLlibcore/util/TimeZoneDataFiles;->getTimeZoneFilePaths(Ljava/lang/String;)[Ljava/lang/String;
-HSPLlibcore/util/TimeZoneFinder$ReaderSupplier;->forFile(Ljava/lang/String;Ljava/nio/charset/Charset;)Llibcore/util/TimeZoneFinder$ReaderSupplier;
-HSPLlibcore/util/TimeZoneFinder$ReaderSupplier;->get()Ljava/io/Reader;
-HSPLlibcore/util/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;->processCountryZones(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Z
-HSPLlibcore/util/TimeZoneFinder$TimeZonesProcessor;->processHeader(Ljava/lang/String;)Z
-HSPLlibcore/util/TimeZoneFinder;->checkOnEndTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;)V
-HSPLlibcore/util/TimeZoneFinder;->consumeText(Lorg/xmlpull/v1/XmlPullParser;)Ljava/lang/String;
-HSPLlibcore/util/TimeZoneFinder;->createInstanceWithFallback([Ljava/lang/String;)Llibcore/util/TimeZoneFinder;
-HSPLlibcore/util/TimeZoneFinder;->findStartTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Z)Z
-HSPLlibcore/util/TimeZoneFinder;->getInstance()Llibcore/util/TimeZoneFinder;
-HSPLlibcore/util/TimeZoneFinder;->lookupCountryTimeZones(Ljava/lang/String;)Llibcore/util/CountryTimeZones;
-HSPLlibcore/util/TimeZoneFinder;->parseBooleanAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean;
-HSPLlibcore/util/TimeZoneFinder;->parseLongAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
-HSPLlibcore/util/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
-HSPLlibcore/util/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/util/TimeZoneFinder$TimeZonesProcessor;)Z
-HSPLlibcore/util/TimeZoneFinder;->processXml(Llibcore/util/TimeZoneFinder$TimeZonesProcessor;)V
HSPLlibcore/util/ZoneInfo$WallTime;-><init>()V
HSPLlibcore/util/ZoneInfo$WallTime;->copyFieldsFromCalendar()V
HSPLlibcore/util/ZoneInfo$WallTime;->copyFieldsToCalendar()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
@@ -63455,13 +63456,21 @@
Llibcore/reflect/TypeVariableImpl;
Llibcore/reflect/Types;
Llibcore/reflect/WildcardTypeImpl;
-Llibcore/util/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;
+Llibcore/timezone/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;
+Llibcore/timezone/CountryTimeZones$OffsetResult;
+Llibcore/timezone/CountryTimeZones$TimeZoneMapping;
+Llibcore/timezone/CountryTimeZones;
+Llibcore/timezone/TimeZoneDataFiles;
+Llibcore/timezone/TimeZoneFinder$ReaderSupplier;
+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;
-Llibcore/util/CountryTimeZones$OffsetResult;
-Llibcore/util/CountryTimeZones$TimeZoneMapping;
-Llibcore/util/CountryTimeZones;
Llibcore/util/EmptyArray;
Llibcore/util/HexEncoding;
Llibcore/util/NativeAllocationRegistry$CleanerRunner;
@@ -63469,18 +63478,10 @@
Llibcore/util/NativeAllocationRegistry;
Llibcore/util/Objects;
Llibcore/util/SneakyThrow;
-Llibcore/util/TimeZoneDataFiles;
-Llibcore/util/TimeZoneFinder$ReaderSupplier;
-Llibcore/util/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
-Llibcore/util/TimeZoneFinder$TimeZonesProcessor;
-Llibcore/util/TimeZoneFinder;
Llibcore/util/ZoneInfo$CheckedArithmeticException;
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-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8a770b9..25bd033 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -189,10 +189,10 @@
Landroid/app/IUiModeManager;->disableCarMode(I)V
Landroid/app/IUserSwitchObserver$Stub;-><init>()V
Landroid/app/IWallpaperManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IWallpaperManager;
-Landroid/app/IWallpaperManager;->getHeightHint()I
+Landroid/app/IWallpaperManager;->getHeightHint(I)I
Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
Landroid/app/IWallpaperManager;->getWallpaperInfo(I)Landroid/app/WallpaperInfo;
-Landroid/app/IWallpaperManager;->getWidthHint()I
+Landroid/app/IWallpaperManager;->getWidthHint(I)I
Landroid/app/IWallpaperManager;->hasNamedWallpaper(Ljava/lang/String;)Z
Landroid/app/IWallpaperManager;->setWallpaperComponent(Landroid/content/ComponentName;)V
Landroid/app/IWallpaperManagerCallback$Stub;-><init>()V
@@ -1435,7 +1435,7 @@
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;
@@ -1447,7 +1447,7 @@
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;->hasNavigationBar(I)Z
Landroid/view/IWindowManager;->isKeyguardLocked()Z
Landroid/view/IWindowManager;->isKeyguardSecure()Z
Landroid/view/IWindowManager;->isSafeModeEnabled()Z
@@ -1461,7 +1461,6 @@
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
@@ -1488,86 +1487,10 @@
Landroid/widget/QuickContactBadge$QueryHandler;-><init>(Landroid/widget/QuickContactBadge;Landroid/content/ContentResolver;)V
Landroid/widget/RelativeLayout$DependencyGraph$Node;-><init>()V
Landroid/widget/ScrollBarDrawable;-><init>()V
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->clear()V
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->getRememberedPosition()I
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigit(C)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigitAndRememberPosition(C)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getDescriptionForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/util/Locale;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getInstance()Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;
-Lcom/android/i18n/phonenumbers/NumberParseException;->getErrorType()Lcom/android/i18n/phonenumbers/NumberParseException$ErrorType;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getDomesticCarrierCodeFormattingRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getFormat()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getLeadingDigitsPattern(I)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getNationalPrefixFormattingRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getPattern()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->leadingDigitsPatternSize()I
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getCountryCode()I
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getGeneralDesc()Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixForParsing()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixTransformRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getPreferredExtnPrefix()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasNationalPrefix()Z
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasPreferredExtnPrefix()Z
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->intlNumberFormats()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->numberFormats()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;-><init>()V
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;->getMetadataList()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;->getNationalNumberPattern()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_DEFAULT_COUNTRY:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITHOUT_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_IDD:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->values()[Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->clearCountryCode()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCode()I
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCodeSource()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getExtension()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getNationalNumber()J
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasCountryCode()Z
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasExtension()Z
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->end()I
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->number()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->rawString()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->start()I
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;->POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->EXACT_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NOT_A_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NO_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->SHORT_NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->E164:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->INTERNATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->NATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->RFC3966:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE_OR_MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PAGER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PERSONAL_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PREMIUM_RATE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->SHARED_COST:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->TOLL_FREE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->UAN:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOICEMAIL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOIP:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->IS_POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->TOO_LONG:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->findNumbers(Ljava/lang/CharSequence;Ljava/lang/String;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;J)Ljava/lang/Iterable;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->format(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->formatInOriginalFormat(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/lang/String;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getAsYouTypeFormatter(Ljava/lang/String;)Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getCountryCodeForRegion(Ljava/lang/String;)I
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getInstance()Lcom/android/i18n/phonenumbers/PhoneNumberUtil;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNationalSignificantNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNumberType(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getRegionCodeForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isNumberMatch(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumberWithReason(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isValidNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
Lcom/android/ims/ImsCall;->deflect(Ljava/lang/String;)V
Lcom/android/ims/ImsCall;->isMultiparty()Z
Lcom/android/ims/ImsCall;->reject(I)V
@@ -2691,7 +2614,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;
@@ -4139,77 +4061,6 @@
Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageScrollStateChanged(I)V
Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageSelected(I)V
Lcom/android/internal/widget/ViewPager;->getCurrentItem()I
-Lcom/android/okhttp/Connection;->getSocket()Ljava/net/Socket;
-Lcom/android/okhttp/ConnectionPool;->connections:Ljava/util/Deque;
-Lcom/android/okhttp/ConnectionPool;->keepAliveDurationNs:J
-Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
-Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/HttpHandler;-><init>()V
-Lcom/android/okhttp/HttpsHandler;-><init>()V
-Lcom/android/okhttp/HttpUrl$Builder;->build()Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/HttpUrl;->encodedPath()Ljava/lang/String;
-Lcom/android/okhttp/HttpUrl;->newBuilder()Lcom/android/okhttp/HttpUrl$Builder;
-Lcom/android/okhttp/HttpUrl;->parse(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/HttpUrl;->query()Ljava/lang/String;
-Lcom/android/okhttp/internal/http/HeaderParser;->skipUntil(Ljava/lang/String;ILjava/lang/String;)I
-Lcom/android/okhttp/internal/http/HeaderParser;->skipWhitespace(Ljava/lang/String;I)I
-Lcom/android/okhttp/internal/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
-Lcom/android/okhttp/internal/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
-Lcom/android/okhttp/internal/http/HttpEngine;->getConnection()Lcom/android/okhttp/Connection;
-Lcom/android/okhttp/internal/http/HttpEngine;->hasResponse()Z
-Lcom/android/okhttp/internal/http/HttpEngine;->httpStream:Lcom/android/okhttp/internal/http/HttpStream;
-Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest(Lcom/android/okhttp/Request;)Lcom/android/okhttp/Request;
-Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest:Lcom/android/okhttp/Request;
-Lcom/android/okhttp/internal/http/HttpEngine;->priorResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/internal/http/HttpEngine;->readResponse()V
-Lcom/android/okhttp/internal/http/HttpEngine;->sendRequest()V
-Lcom/android/okhttp/internal/http/HttpEngine;->sentRequestMillis:J
-Lcom/android/okhttp/internal/http/HttpEngine;->userResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/internal/http/HttpEngine;->writingRequestHeaders()V
-Lcom/android/okhttp/internal/http/RouteSelector;->hasNext()Z
-Lcom/android/okhttp/internal/huc/HttpsURLConnectionImpl;->delegate:Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;
-Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->client:Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->httpEngine:Lcom/android/okhttp/internal/http/HttpEngine;
-Lcom/android/okhttp/internal/Internal;-><init>()V
-Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Internal;->apply(Lcom/android/okhttp/ConnectionSpec;Ljavax/net/ssl/SSLSocket;Z)V
-Lcom/android/okhttp/internal/Internal;->callEngineGetStreamAllocation(Lcom/android/okhttp/Call;)Lcom/android/okhttp/internal/http/StreamAllocation;
-Lcom/android/okhttp/internal/Internal;->callEnqueue(Lcom/android/okhttp/Call;Lcom/android/okhttp/Callback;Z)V
-Lcom/android/okhttp/internal/Internal;->connectionBecameIdle(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)Z
-Lcom/android/okhttp/internal/Internal;->get(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/Address;Lcom/android/okhttp/internal/http/StreamAllocation;)Lcom/android/okhttp/internal/io/RealConnection;
-Lcom/android/okhttp/internal/Internal;->getHttpUrlChecked(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/internal/Internal;->instance:Lcom/android/okhttp/internal/Internal;
-Lcom/android/okhttp/internal/Internal;->internalCache(Lcom/android/okhttp/OkHttpClient;)Lcom/android/okhttp/internal/InternalCache;
-Lcom/android/okhttp/internal/Internal;->put(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)V
-Lcom/android/okhttp/internal/Internal;->routeDatabase(Lcom/android/okhttp/ConnectionPool;)Lcom/android/okhttp/internal/RouteDatabase;
-Lcom/android/okhttp/internal/Internal;->setCache(Lcom/android/okhttp/OkHttpClient;Lcom/android/okhttp/internal/InternalCache;)V
-Lcom/android/okhttp/internal/Platform;->get()Lcom/android/okhttp/internal/Platform;
-Lcom/android/okhttp/internal/Platform;->logW(Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Util;->closeAll(Ljava/io/Closeable;Ljava/io/Closeable;)V
-Lcom/android/okhttp/internal/Util;->closeQuietly(Ljava/io/Closeable;)V
-Lcom/android/okhttp/internal/Util;->EMPTY_BYTE_ARRAY:[B
-Lcom/android/okhttp/internal/Util;->UTF_8:Ljava/nio/charset/Charset;
-Lcom/android/okhttp/OkHttpClient;-><init>()V
-Lcom/android/okhttp/OkHttpClient;->connectionPool:Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/OkHttpClient;->DEFAULT_PROTOCOLS:Ljava/util/List;
-Lcom/android/okhttp/OkHttpClient;->dns:Lcom/android/okhttp/Dns;
-Lcom/android/okhttp/OkHttpClient;->getConnectionPool()Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/OkHttpClient;->getCookieHandler()Ljava/net/CookieHandler;
-Lcom/android/okhttp/OkHttpClient;->getHostnameVerifier()Ljavax/net/ssl/HostnameVerifier;
-Lcom/android/okhttp/OkHttpClient;->getProxy()Ljava/net/Proxy;
-Lcom/android/okhttp/OkHttpClient;->getProxySelector()Ljava/net/ProxySelector;
-Lcom/android/okhttp/OkHttpClient;->getSslSocketFactory()Ljavax/net/ssl/SSLSocketFactory;
-Lcom/android/okhttp/OkHttpClient;->setProtocols(Ljava/util/List;)Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/OkHttpClient;->setRetryOnConnectionFailure(Z)V
-Lcom/android/okhttp/Request;->headers:Lcom/android/okhttp/Headers;
-Lcom/android/okhttp/Request;->method:Ljava/lang/String;
-Lcom/android/okhttp/Request;->url:Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/Response;->code:I
-Lcom/android/okhttp/Response;->headers:Lcom/android/okhttp/Headers;
-Lcom/android/okhttp/Response;->message:Ljava/lang/String;
-Lcom/android/okhttp/Response;->networkResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/Response;->protocol:Lcom/android/okhttp/Protocol;
Lcom/android/server/net/BaseNetworkObserver;-><init>()V
Lcom/android/server/net/NetlinkTracker;-><init>(Ljava/lang/String;Lcom/android/server/net/NetlinkTracker$Callback;)V
Lcom/android/server/net/NetlinkTracker;->clearLinkProperties()V
@@ -4515,7 +4366,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
@@ -4655,178 +4505,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;
@@ -5505,431 +5186,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
index 575ba34..a21fa30 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -147,8 +147,8 @@
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;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)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
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 553acc8..05bb9a1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -827,6 +827,8 @@
/** The screen observation manager. Always access via {@link #getIntelligenceManager()}. */
@Nullable private IntelligenceManager mIntelligenceManager;
+ private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
+ new ArrayList<Application.ActivityLifecycleCallbacks>();
static final class NonConfigurationInstances {
Object activity;
@@ -1065,6 +1067,288 @@
}
/**
+ * 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
* activity's UI, using {@link #findViewById} to programmatically interact
@@ -1119,7 +1403,7 @@
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
- getApplication().dispatchActivityCreated(this, savedInstanceState);
+ dispatchActivityCreated(savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
@@ -1355,7 +1639,7 @@
mFragments.doLoaderStart();
- getApplication().dispatchActivityStarted(this);
+ dispatchActivityStarted();
if (mAutoFillResetNeeded) {
getAutofillManager().onVisibleForAutofill();
@@ -1426,13 +1710,21 @@
@CallSuper
protected void onResume() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
- getApplication().dispatchActivityResumed(this);
+ dispatchActivityResumed();
mActivityTransitionState.onResume(this, isTopOfTask());
enableAutofillCompatibilityIfNeeded();
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
View focus = getCurrentFocus();
- if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+ // On Activity rotation situation (mRestoredFromBundle is true),
+ // we should not call on AutofillManager in onResume()
+ // since the next Layout pass will do that.
+ // However, there are both cases where Activity#getCurrentFocus()
+ // will return null (window not preserved) and not null (window IS
+ // preserved), so we need to explicitly check for mRestoredFromBundle
+ // here.
+ if (!mRestoredFromBundle && focus != null
+ && focus.canNotifyAutofillEnterExitEvent()) {
// TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
// testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
// window visibility after recreation is INVISIBLE in onResume() and next frame
@@ -1634,13 +1926,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);
}
/**
@@ -1654,13 +1946,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);
}
/**
@@ -1723,7 +2015,7 @@
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
- getApplication().dispatchActivitySaveInstanceState(this, outState);
+ dispatchActivitySaveInstanceState(outState);
}
/**
@@ -1823,7 +2115,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);
@@ -2007,7 +2299,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;
@@ -2096,7 +2388,7 @@
mActionBar.onDestroy();
}
- getApplication().dispatchActivityDestroyed(this);
+ dispatchActivityDestroyed();
notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
@@ -7276,7 +7568,7 @@
@UnsupportedAppUsage
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
- getApplication().dispatchActivityPreCreated(this, icicle);
+ dispatchActivityPreCreated(icicle);
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
@@ -7291,7 +7583,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) {
@@ -7300,7 +7592,7 @@
}
final void performStart(String reason) {
- getApplication().dispatchActivityPreStarted(this);
+ dispatchActivityPreStarted();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
mFragments.noteStateNotSaved();
mCalled = false;
@@ -7343,7 +7635,7 @@
}
mActivityTransitionState.enterReady(this);
- getApplication().dispatchActivityPostStarted(this);
+ dispatchActivityPostStarted();
}
/**
@@ -7398,7 +7690,7 @@
}
final void performResume(boolean followedByPause, String reason) {
- getApplication().dispatchActivityPreResumed(this);
+ dispatchActivityPreResumed();
performRestart(true /* start */, reason);
mFragments.execPendingActions();
@@ -7448,11 +7740,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;
@@ -7465,7 +7757,7 @@
"Activity " + mComponent.toShortString() +
" did not call through to super.onPause()");
}
- getApplication().dispatchActivityPostPaused(this);
+ dispatchActivityPostPaused();
}
final void performUserLeaving() {
@@ -7481,7 +7773,7 @@
mCanEnterPictureInPicture = false;
if (!mStopped) {
- getApplication().dispatchActivityPreStopped(this);
+ dispatchActivityPreStopped();
if (mWindow != null) {
mWindow.closeAllPanels();
}
@@ -7516,13 +7808,13 @@
}
mStopped = true;
- getApplication().dispatchActivityPostStopped(this);
+ dispatchActivityPostStopped();
}
mResumed = false;
}
final void performDestroy() {
- getApplication().dispatchActivityPreDestroyed(this);
+ dispatchActivityPreDestroyed();
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
@@ -7532,7 +7824,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/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 805fb68..41166dd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -157,7 +157,6 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -5324,16 +5323,6 @@
}
}
- /**
- * Updates the application info.
- *
- * This only works in the system process. Must be called on the main thread.
- */
- public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) {
- Preconditions.checkState(mSystemThread, "Must only be called in the system process");
- handleApplicationInfoChanged(ai);
- }
-
void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
// Updates triggered by package installation go through a package update
// receiver. Here we try to capture ApplicationInfo changes that are
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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 63a41ec..3069be6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1180,11 +1180,11 @@
Manifest.permission.ACTIVITY_RECOGNITION,
Manifest.permission.SMS_FINANCIAL_TRANSACTIONS,
Manifest.permission.READ_MEDIA_AUDIO,
- Manifest.permission.WRITE_MEDIA_AUDIO,
+ null, // no permission for OP_WRITE_MEDIA_AUDIO
Manifest.permission.READ_MEDIA_VIDEO,
- Manifest.permission.WRITE_MEDIA_VIDEO,
+ null, // no permission for OP_WRITE_MEDIA_VIDEO
Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.WRITE_MEDIA_IMAGES,
+ null, // no permission for OP_WRITE_MEDIA_IMAGES
};
/**
@@ -1462,11 +1462,11 @@
AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION
AppOpsManager.MODE_DEFAULT, // SMS_FINANCIAL_TRANSACTIONS
AppOpsManager.MODE_ALLOWED, // READ_MEDIA_AUDIO
- AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_AUDIO
+ AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_AUDIO
AppOpsManager.MODE_ALLOWED, // READ_MEDIA_VIDEO
- AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_VIDEO
+ AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_VIDEO
AppOpsManager.MODE_ALLOWED, // READ_MEDIA_IMAGES
- AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_IMAGES
+ AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES
};
/**
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index 30d6bee..9ef24c6 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -27,6 +27,7 @@
import dalvik.system.PathClassLoader;
import java.util.Collection;
+import java.util.List;
/** @hide */
public class ApplicationLoaders {
@@ -38,15 +39,25 @@
ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String classLoaderName) {
+ return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
+ librarySearchPath, libraryPermittedPath, parent, classLoaderName,
+ null);
+ }
+
+ ClassLoader getClassLoaderWithSharedLibraries(
+ String zip, int targetSdkVersion, boolean isBundled,
+ String librarySearchPath, String libraryPermittedPath,
+ ClassLoader parent, String classLoaderName,
+ List<ClassLoader> sharedLibraries) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
- libraryPermittedPath, parent, zip, classLoaderName);
+ libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
}
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String cacheKey,
- String classLoaderName) {
+ String classLoaderName, List<ClassLoader> sharedLibraries) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -75,7 +86,7 @@
ClassLoader classloader = ClassLoaderFactory.createClassLoader(
zip, librarySearchPath, libraryPermittedPath, parent,
- targetSdkVersion, isBundled, classLoaderName);
+ targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -90,7 +101,7 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
ClassLoader loader = ClassLoaderFactory.createClassLoader(
- zip, null, parent, classLoaderName);
+ zip, null, parent, classLoaderName, sharedLibraries);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return loader;
}
@@ -110,7 +121,7 @@
// The cache key is passed separately to enable the stub WebView to be cached under the
// stub's APK path, when the actual package path is the donor APK.
return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
- cacheKey, null /* classLoaderName */);
+ cacheKey, null /* classLoaderName */, null /* sharedLibraries */);
}
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8bb704d..7312b2c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2046,8 +2046,6 @@
StorageManager storage) {
if (app.isInternal()) {
return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
- } else if (app.isExternalAsec()) {
- return storage.getPrimaryPhysicalVolume();
} else {
return storage.findVolumeByUuid(app.volumeUuid);
}
@@ -2983,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 9837deb..28ecb27 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1724,6 +1724,24 @@
}
@Override
+ public void updateServiceGroup(@NonNull ServiceConnection conn, int group, int importance) {
+ if (conn == null) {
+ throw new IllegalArgumentException("connection is null");
+ }
+ if (mPackageInfo != null) {
+ IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+ getOuterContext(), conn);
+ try {
+ ActivityManager.getService().updateServiceGroup(sd, group, importance);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ throw new RuntimeException("Not supported in system context");
+ }
+ }
+
+ @Override
public void unbindService(ServiceConnection conn) {
if (conn == null) {
throw new IllegalArgumentException("connection is null");
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f27c667..e83bcd0 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -140,6 +140,7 @@
int bindIsolatedService(in IApplicationThread caller, in IBinder token, in Intent service,
in String resolvedType, in IServiceConnection connection, int flags,
in String instanceName, in String callingPackage, int userId);
+ void updateServiceGroup(in IServiceConnection connection, int group, int importance);
boolean unbindService(in IServiceConnection connection);
void publishService(in IBinder token, in Intent intent, in IBinder service);
void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
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/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 5ef4be1..3a2038d 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -87,24 +87,24 @@
/**
* Sets the dimension hint for the wallpaper. These hints indicate the desired
- * minimum width and height for the wallpaper.
+ * minimum width and height for the wallpaper in a particular display.
*/
- void setDimensionHints(in int width, in int height, in String callingPackage);
+ void setDimensionHints(in int width, in int height, in String callingPackage, int displayId);
/**
- * Returns the desired minimum width for the wallpaper.
+ * Returns the desired minimum width for the wallpaper in a particular display.
*/
- int getWidthHint();
+ int getWidthHint(int displayId);
/**
- * Returns the desired minimum height for the wallpaper.
+ * Returns the desired minimum height for the wallpaper in a particular display.
*/
- int getHeightHint();
+ int getHeightHint(int displayId);
/**
* Sets extra padding that we would like the wallpaper to have outside of the display.
*/
- void setDisplayPadding(in Rect padding, in String callingPackage);
+ void setDisplayPadding(in Rect padding, in String callingPackage, int displayId);
/**
* Returns the name of the wallpaper. Private API.
@@ -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 da4f77b..3f10754 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -29,6 +29,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.SharedLibraryInfo;
import android.content.pm.dex.ArtManager;
import android.content.pm.split.SplitDependencyLoader;
import android.content.res.AssetManager;
@@ -70,8 +71,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
final class IntentReceiverLeaked extends AndroidRuntimeException {
@UnsupportedAppUsage
@@ -397,6 +400,24 @@
makePaths(activityThread, false, aInfo, outZipPaths, null);
}
+ private static void appendSharedLibrariesLibPathsIfNeeded(
+ List<SharedLibraryInfo> sharedLibraries, ApplicationInfo aInfo,
+ Set<String> outSeenPaths,
+ List<String> outLibPaths) {
+ if (sharedLibraries == null) {
+ return;
+ }
+ for (SharedLibraryInfo lib : sharedLibraries) {
+ List<String> paths = lib.getAllCodePaths();
+ outSeenPaths.addAll(paths);
+ for (String path : paths) {
+ appendApkLibPathIfNeeded(path, aInfo, outLibPaths);
+ }
+ appendSharedLibrariesLibPathsIfNeeded(
+ lib.getDependencies(), aInfo, outSeenPaths, outLibPaths);
+ }
+ }
+
public static void makePaths(ActivityThread activityThread,
boolean isBundledApp,
ApplicationInfo aInfo,
@@ -404,7 +425,6 @@
List<String> outLibPaths) {
final String appDir = aInfo.sourceDir;
final String libDir = aInfo.nativeLibraryDir;
- final String[] sharedLibraries = aInfo.sharedLibraryFiles;
outZipPaths.clear();
outZipPaths.add(appDir);
@@ -499,11 +519,19 @@
}
}
- // Prepend the shared libraries, maintaining their original order where possible.
- if (sharedLibraries != null) {
+ // Add the shared libraries native paths. The dex files in shared libraries will
+ // be resolved through shared library loaders, which are setup later.
+ Set<String> outSeenPaths = new LinkedHashSet<>();
+ appendSharedLibrariesLibPathsIfNeeded(
+ aInfo.sharedLibraryInfos, aInfo, outSeenPaths, outLibPaths);
+
+ // ApplicationInfo.sharedLibraryFiles is a public API, so anyone can change it.
+ // We prepend shared libraries that the package manager hasn't seen, maintaining their
+ // original order where possible.
+ if (aInfo.sharedLibraryFiles != null) {
int index = 0;
- for (String lib : sharedLibraries) {
- if (!outZipPaths.contains(lib)) {
+ for (String lib : aInfo.sharedLibraryFiles) {
+ if (!outSeenPaths.contains(lib) && !outZipPaths.contains(lib)) {
outZipPaths.add(index, lib);
index++;
appendApkLibPathIfNeeded(lib, aInfo, outLibPaths);
@@ -631,6 +659,43 @@
return mSplitLoader.getSplitPathsForSplit(splitName);
}
+ /**
+ * Create a class loader for the {@code sharedLibrary}. Shared libraries are canonicalized,
+ * so if we already created a class loader with that shared library, we return it.
+ *
+ * Implementation notes: the canonicalization of shared libraries is something dex2oat
+ * also does.
+ */
+ ClassLoader createSharedLibraryLoader(SharedLibraryInfo sharedLibrary,
+ boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) {
+ List<String> paths = sharedLibrary.getAllCodePaths();
+ List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
+ sharedLibrary.getDependencies(), isBundledApp, librarySearchPath,
+ libraryPermittedPath);
+ final String jars = (paths.size() == 1) ? paths.get(0) :
+ TextUtils.join(File.pathSeparator, paths);
+
+ // Shared libraries get a null parent: this has the side effect of having canonicalized
+ // shared libraries using ApplicationLoaders cache, which is the behavior we want.
+ return ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(jars,
+ mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
+ libraryPermittedPath, /* parent */ null,
+ /* classLoaderName */ null, sharedLibraries);
+ }
+
+ private List<ClassLoader> createSharedLibrariesLoaders(List<SharedLibraryInfo> sharedLibraries,
+ boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) {
+ if (sharedLibraries == null) {
+ return null;
+ }
+ List<ClassLoader> loaders = new ArrayList<>();
+ for (SharedLibraryInfo info : sharedLibraries) {
+ loaders.add(createSharedLibraryLoader(
+ info, isBundledApp, librarySearchPath, libraryPermittedPath));
+ }
+ return loaders;
+ }
+
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
@@ -759,10 +824,14 @@
// as this is early and necessary.
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
- mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
- mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
+ List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
+ mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
+ libraryPermittedPath);
+
+ mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
+ zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
- mApplicationInfo.classLoaderName);
+ mApplicationInfo.classLoaderName, sharedLibraries);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
StrictMode.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 450efdf..5002a81 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1338,6 +1338,11 @@
private int mBadgeIcon = BADGE_ICON_NONE;
/**
+ * Determines whether the platform can generate contextual actions for a notification.
+ */
+ private boolean mAllowSystemGeneratedContextualActions = true;
+
+ /**
* Structure to encapsulate a named action that can be shown as part of this notification.
* It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
* selected by the user.
@@ -2238,6 +2243,8 @@
if (parcel.readInt() != 0) {
mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
}
+
+ mAllowSystemGeneratedContextualActions = parcel.readBoolean();
}
@Override
@@ -2353,6 +2360,7 @@
that.mSettingsText = this.mSettingsText;
that.mGroupAlertBehavior = this.mGroupAlertBehavior;
that.mAppOverlayIntent = this.mAppOverlayIntent;
+ that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;
if (!heavy) {
that.lightenPayload(); // will clean out extras
@@ -2681,6 +2689,8 @@
parcel.writeInt(0);
}
+ parcel.writeBoolean(mAllowSystemGeneratedContextualActions);
+
// mUsesStandardHeader is not written because it should be recomputed in listeners
}
@@ -3101,6 +3111,10 @@
return mAppOverlayIntent;
}
+ public boolean getAllowSystemGeneratedContextualActions() {
+ return mAllowSystemGeneratedContextualActions;
+ }
+
/**
* The small icon representing this notification in the status bar and content view.
*
@@ -3193,6 +3207,25 @@
}
/**
+ * Returns the actions that are contextual (marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) out
+ * of the actions in this notification.
+ *
+ * @hide
+ */
+ public List<Notification.Action> getContextualActions() {
+ if (actions == null) return Collections.emptyList();
+
+ List<Notification.Action> contextualActions = new ArrayList<>();
+ for (Notification.Action action : actions) {
+ if (action.getSemanticAction()
+ == Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+ contextualActions.add(action);
+ }
+ }
+ return contextualActions;
+ }
+
+ /**
* Builder class for {@link Notification} objects.
*
* Provides a convenient way to set the various fields of a {@link Notification} and generate
@@ -4403,15 +4436,15 @@
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);
}
}
}
@@ -4474,16 +4507,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);
@@ -4491,15 +4514,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
@@ -4510,7 +4533,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);
}
@@ -4527,8 +4550,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);
}
@@ -4537,42 +4561,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(
@@ -4640,10 +4685,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",
@@ -4666,19 +4712,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);
}
@@ -4691,8 +4738,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);
@@ -4740,15 +4787,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;
}
@@ -4757,8 +4804,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();
@@ -4768,7 +4815,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);
@@ -4795,41 +4842,41 @@
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);
}
- 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",
@@ -4837,11 +4884,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
@@ -4850,36 +4897,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);
}
}
@@ -4917,23 +4964,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);
}
/**
@@ -5008,8 +5059,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);
@@ -5030,20 +5080,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) {
@@ -5051,7 +5101,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);
}
}
}
@@ -5142,18 +5192,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;
}
@@ -5296,24 +5351,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;
}
@@ -5342,7 +5388,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()
@@ -5359,7 +5405,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 {
@@ -5367,7 +5413,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) {
@@ -5378,11 +5424,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;
@@ -5394,13 +5441,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;
}
@@ -5506,15 +5555,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,
@@ -5530,11 +5579,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);
}
}
@@ -5545,29 +5595,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;
@@ -5583,13 +5647,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;
}
@@ -5638,6 +5703,15 @@
}
/**
+ * Determines whether the platform can generate contextual actions for a notification.
+ * By default this is true.
+ */
+ public Builder setAllowSystemGeneratedContextualActions(boolean allowed) {
+ mN.mAllowSystemGeneratedContextualActions = allowed;
+ return this;
+ }
+
+ /**
* @deprecated Use {@link #build()} instead.
*/
@Deprecated
@@ -5812,9 +5886,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;
}
@@ -5822,10 +5896,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();
}
@@ -5833,9 +5908,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);
@@ -5843,10 +5919,6 @@
return backgroundColor;
}
- private boolean isColorized() {
- return mN.isColorized();
- }
-
private boolean shouldTintActionButtons() {
return mTintActionButtons;
}
@@ -5872,7 +5944,7 @@
mBackgroundColor = backgroundColor;
mForegroundColor = foregroundColor;
mTextColorsAreForBackground = COLOR_INVALID;
- ensureColors();
+ ensureColors(mParams.reset().fillTextsFrom(this));
}
/**
@@ -6140,30 +6212,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);
@@ -6458,12 +6530,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());
@@ -6656,24 +6729,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;
}
@@ -6691,14 +6764,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());
- }
}
/**
@@ -7182,24 +7247,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",
@@ -7461,7 +7528,11 @@
return mRemoteInputHistory;
}
- private Bundle toBundle() {
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public Bundle toBundle() {
Bundle bundle = new Bundle();
if (mText != null) {
bundle.putCharSequence(KEY_TEXT, mText);
@@ -7659,15 +7730,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};
@@ -7714,7 +7779,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());
@@ -7951,7 +8016,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());
@@ -7962,8 +8027,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);
@@ -7985,8 +8050,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();
@@ -8001,7 +8068,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);
}
@@ -8016,9 +8083,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() {
@@ -8030,13 +8097,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);
}
@@ -8292,7 +8360,7 @@
// 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();
@@ -9835,17 +9903,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;
}
@@ -9864,6 +9938,11 @@
return this;
}
+ final StandardTemplateParams summaryText(CharSequence text) {
+ this.summaryText = text;
+ return this;
+ }
+
final StandardTemplateParams headerTextSecondary(CharSequence text) {
this.headerTextSecondary = text;
return this;
@@ -9879,6 +9958,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;
@@ -9895,6 +9984,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/Person.java b/core/java/android/app/Person.java
index 3884a8d..0abc998 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -22,6 +22,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Provides an immutable reference to an entity that appears repeatedly on different surfaces of the
* platform. For example, this could represent the sender of a message.
@@ -121,6 +123,26 @@
}
@Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Person) {
+ final Person other = (Person) obj;
+ return Objects.equals(mName, other.mName)
+ && (mIcon == null ? other.mIcon == null :
+ (other.mIcon != null && mIcon.sameAs(other.mIcon)))
+ && Objects.equals(mUri, other.mUri)
+ && Objects.equals(mKey, other.mKey)
+ && mIsBot == other.mIsBot
+ && mIsImportant == other.mIsImportant;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mIcon, mUri, mKey, mIsBot, mIsImportant);
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index e33d1fe..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;
@@ -36,6 +37,7 @@
import android.util.AttributeSet;
import android.util.Printer;
import android.util.Xml;
+import android.view.SurfaceHolder;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -79,6 +81,7 @@
final boolean mShowMetadataInPreview;
final boolean mSupportsAmbientMode;
final String mSettingsSliceUri;
+ final boolean mSupportMultipleDisplays;
/**
* Constructor.
@@ -143,6 +146,9 @@
false);
mSettingsSliceUri = sa.getString(
com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
+ mSupportMultipleDisplays = sa.getBoolean(
+ com.android.internal.R.styleable.Wallpaper_supportsMultipleDisplays,
+ false);
sa.recycle();
} catch (NameNotFoundException e) {
@@ -163,6 +169,7 @@
mShowMetadataInPreview = source.readInt() != 0;
mSupportsAmbientMode = source.readInt() != 0;
mSettingsSliceUri = source.readString();
+ mSupportMultipleDisplays = source.readInt() != 0;
mService = ResolveInfo.CREATOR.createFromParcel(source);
}
@@ -324,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;
}
@@ -358,6 +367,19 @@
return Uri.parse(mSettingsSliceUri);
}
+ /**
+ * Returns whether this wallpaper service can support multiple engines to render on each surface
+ * independently. An example use case is a multi-display set-up where the wallpaper service can
+ * render surfaces to each of the connected displays.
+ *
+ * @see WallpaperService#onCreateEngine()
+ * @see WallpaperService.Engine#onCreate(SurfaceHolder)
+ * @return {@code true} if multiple engines can render independently on each surface.
+ */
+ public boolean supportsMultipleDisplays() {
+ return mSupportMultipleDisplays;
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "Service:");
mService.dump(pw, prefix + " ");
@@ -387,6 +409,7 @@
dest.writeInt(mShowMetadataInPreview ? 1 : 0);
dest.writeInt(mSupportsAmbientMode ? 1 : 0);
dest.writeString(mSettingsSliceUri);
+ dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
mService.writeToParcel(dest, flags);
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index bebe79e..27471ca 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1485,7 +1485,7 @@
throw new RuntimeException(new DeadSystemException());
}
try {
- return sGlobals.mService.getWidthHint();
+ return sGlobals.mService.getWidthHint(mContext.getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1511,7 +1511,7 @@
throw new RuntimeException(new DeadSystemException());
}
try {
- return sGlobals.mService.getHeightHint();
+ return sGlobals.mService.getHeightHint(mContext.getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1572,7 +1572,7 @@
throw new RuntimeException(new DeadSystemException());
} else {
sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getDisplayId());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1597,7 +1597,8 @@
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
} else {
- sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName());
+ sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName(),
+ mContext.getDisplayId());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f129a71..98d2a40 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -53,6 +53,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
@@ -87,6 +88,7 @@
import com.android.org.conscrypt.TrustedCertificateStore;
import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1930,6 +1932,48 @@
public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3;
/**
+ * Callback used in {@link #installSystemUpdate} to indicate that there was an error while
+ * trying to install an update.
+ */
+ public abstract static class InstallUpdateCallback {
+ /** Represents an unknown error while trying to install an update. */
+ public static final int UPDATE_ERROR_UNKNOWN = 1;
+
+ /** Represents the update file being intended for different OS version. */
+ public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2;
+
+ /**
+ * Represents the update file being wrong, i.e. payloads are mismatched, wrong compressions
+ * method.
+ */
+ public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3;
+
+ /** Represents that the file could not be found. */
+ public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4;
+
+ /** Represents the battery being too low to apply an update. */
+ public static final int UPDATE_ERROR_BATTERY_LOW = 5;
+
+ /** Method invoked when there was an error while installing an update. */
+ public void onInstallUpdateError(
+ @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "UPDATE_ERROR_" }, value = {
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+ InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION,
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+ InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND,
+ InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InstallUpdateCallbackErrorConstants {}
+
+ /**
* Return true if the given administrator component is currently active (enabled) in the system.
*
* @param admin The administrator component to check for.
@@ -6796,7 +6840,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface CreateAndManageUserFlags {}
-
/**
* Called by a device owner to create a user with the specified name and a given component of
* the calling package as profile owner. The UserHandle returned by this method should not be
@@ -9792,7 +9835,6 @@
}
}
-
/**
* Sets the global Private DNS mode and host to be used.
* May only be called by the device owner.
@@ -9828,6 +9870,62 @@
}
/**
+ * Called by device owner to install a system update from the given file. The device will be
+ * rebooted in order to finish installing the update. Note that if the device is rebooted, this
+ * doesn't necessarily mean that the update has been applied successfully. The caller should
+ * additionally check the system version with {@link android.os.Build#FINGERPRINT} or {@link
+ * android.os.Build.VERSION}. If an error occurs during processing the OTA before the reboot,
+ * the caller will be notified by {@link InstallUpdateCallback}. If device does not have
+ * sufficient battery level, the installation will fail with error {@link
+ * InstallUpdateCallback#UPDATE_ERROR_BATTERY_LOW}.
+ *
+ * @param admin The {@link DeviceAdminReceiver} that this request is associated with.
+ * @param updateFilePath An Uri of the file that contains the update. The file should be
+ * readable by the calling app.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback A callback object that will inform the caller when installing an update
+ * fails.
+ */
+ public void installSystemUpdate(
+ @NonNull ComponentName admin, @NonNull Uri updateFilePath,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull InstallUpdateCallback callback) {
+ throwIfParentInstance("installUpdate");
+ if (mService == null) {
+ return;
+ }
+ try (ParcelFileDescriptor fileDescriptor = mContext.getContentResolver()
+ .openFileDescriptor(updateFilePath, "r")) {
+ mService.installUpdateFromFile(
+ admin, fileDescriptor, new StartInstallingUpdateCallback.Stub() {
+ @Override
+ public void onStartInstallingUpdateError(
+ int errorCode, String errorMessage) {
+ executeCallback(errorCode, errorMessage, executor, callback);
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, e);
+ executeCallback(
+ InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND, Log.getStackTraceString(e),
+ executor, callback);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ executeCallback(
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e),
+ executor, callback);
+ }
+ }
+
+ private void executeCallback(int errorCode, String errorMessage,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull InstallUpdateCallback callback) {
+ executor.execute(() -> callback.onInstallUpdateError(errorCode, errorMessage));
+ }
+
+ /**
* Returns the system-wide Private DNS mode.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with.
@@ -9867,4 +9965,138 @@
throw re.rethrowFromSystemServer();
}
}
+
+ /**
+ * Grants the profile owner of the given user access to device identifiers (such as
+ * serial number, IMEI and MEID).
+ *
+ * <p>This lets the profile owner request inclusion of device identifiers when calling
+ * {@link generateKeyPair}.
+ *
+ * <p>This grant is necessary to guarantee that profile owners can access device identifiers.
+ *
+ * <p>Privileged system API - meant to be called by the system, particularly the managed
+ * provisioning app, when a work profile is set up.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void setProfileOwnerCanAccessDeviceIdsForUser(
+ @NonNull ComponentName who, @NonNull UserHandle userHandle) {
+ if (mService == null) {
+ return;
+ }
+ try {
+ mService.grantDeviceIdsAccessToProfileOwner(who, userHandle.getIdentifier());
+ } catch (RemoteException re) {
+ 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,
+ mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ce1f4ef..297676d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -20,6 +20,7 @@
import android.app.admin.NetworkEvent;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
+import android.app.admin.StartInstallingUpdateCallback;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.PasswordMetrics;
@@ -417,4 +418,13 @@
void setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
int getGlobalPrivateDnsMode(in ComponentName admin);
String getGlobalPrivateDnsHost(in ComponentName admin);
+
+ 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);
}
diff --git a/core/java/android/app/admin/StartInstallingUpdateCallback.aidl b/core/java/android/app/admin/StartInstallingUpdateCallback.aidl
new file mode 100644
index 0000000..df04707
--- /dev/null
+++ b/core/java/android/app/admin/StartInstallingUpdateCallback.aidl
@@ -0,0 +1,27 @@
+/*
+**
+** 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.app.admin;
+
+/**
+* Callback used between {@link DevicePolicyManager} and {@link DevicePolicyManagerService} to
+* indicate that starting installing an update is finished.
+* {@hide}
+*/
+oneway interface StartInstallingUpdateCallback {
+ void onStartInstallingUpdateError(int errorCode, String errorMessage);
+}
\ No newline at end of file
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/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 2cf13ec2..3ca8ec0 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -37,6 +37,8 @@
void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback);
+ void setRoleNamesFromController(in List<String> roleNames);
+
boolean addRoleHolderFromController(in String roleName, in String packageName);
boolean removeRoleHolderFromController(in String roleName, in String packageName);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index ef86b01..7cb245a 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -67,10 +67,33 @@
/**
* The name of the SMS role.
+ *
+ * @see Intent#CATEGORY_APP_MESSAGING
*/
public static final String ROLE_SMS = "android.app.role.SMS";
/**
+ * The name of the browser role.
+ *
+ * @see Intent#CATEGORY_APP_BROWSER
+ */
+ public static final String ROLE_BROWSER = "android.app.role.BROWSER";
+
+ /**
+ * The name of the gallery role.
+ *
+ * @see Intent#CATEGORY_APP_GALLERY
+ */
+ public static final String ROLE_GALLERY = "android.app.role.GALLERY";
+
+ /**
+ * The name of the music player role.
+ *
+ * @see Intent#CATEGORY_APP_MUSIC
+ */
+ public static final String ROLE_MUSIC = "android.app.role.MUSIC";
+
+ /**
* The action used to request user approval of a role for an application.
*
* @hide
@@ -92,8 +115,8 @@
*
* @hide
*/
- public static final String PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER =
- "com.android.permissioncontroller.permission.MANAGE_ROLE_HOLDERS_FROM_CONTROLLER";
+ public static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER =
+ "com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER";
@NonNull
private final Context mContext;
@@ -342,12 +365,36 @@
}
/**
+ * Set the names of all the available roles. Should only be called from
+ * {@link android.rolecontrollerservice.RoleControllerService}.
+ * <p>
+ * <strong>Note:</strong> Using this API requires holding
+ * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
+ *
+ * @param roleNames the names of all the available roles
+ *
+ * @throws IllegalArgumentException if the list of role names is {@code null}.
+ *
+ * @hide
+ */
+ @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
+ @SystemApi
+ public void setRoleNamesFromController(@NonNull List<String> roleNames) {
+ Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
+ try {
+ mService.setRoleNamesFromController(roleNames);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Add a specific application to the holders of a role, only modifying records inside
* {@link RoleManager}. Should only be called from
* {@link android.rolecontrollerservice.RoleControllerService}.
* <p>
* <strong>Note:</strong> Using this API requires holding
- * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
+ * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
*
* @param roleName the name of the role to add the role holder for
* @param packageName the package name of the application to add to the role holders
@@ -362,7 +409,7 @@
*
* @hide
*/
- @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
+ @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
public boolean addRoleHolderFromController(@NonNull String roleName,
@NonNull String packageName) {
@@ -381,7 +428,7 @@
* {@link android.rolecontrollerservice.RoleControllerService}.
* <p>
* <strong>Note:</strong> Using this API requires holding
- * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
+ * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
*
* @param roleName the name of the role to remove the role holder for
* @param packageName the package name of the application to remove from the role holders
@@ -396,7 +443,7 @@
*
* @hide
*/
- @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
+ @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
public boolean removeRoleHolderFromController(@NonNull String roleName,
@NonNull String packageName) {
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5514851..3f34803 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -193,10 +193,6 @@
/** @hide */
public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000D;
/** @hide */
- public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000E;
- /** @hide */
- public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP = 0x000F;
- /** @hide */
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 0aa0535..235dc5c 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -347,7 +347,7 @@
* device are requested to be fetched using Service Discovery Protocol
* <p> Always contains the extra field {@link #EXTRA_DEVICE}
* <p> Always contains the extra field {@link #EXTRA_UUID}
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to receive.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_UUID =
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 636b1b9..8d9d340 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -973,11 +973,11 @@
*/
@UnsupportedAppUsage
public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
- int type) {
+ int type, String name) {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- service.phoneStateChanged(numActive, numHeld, callState, number, type);
+ service.phoneStateChanged(numActive, numHeld, callState, number, type, name);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 47c4ee6..6ed7942 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -51,7 +51,7 @@
*/
public final class BluetoothHearingAid implements BluetoothProfile {
private static final String TAG = "BluetoothHearingAid";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final boolean VDBG = false;
/**
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 949cdd6..437039d 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -42,7 +42,6 @@
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
@@ -3244,18 +3243,8 @@
Objects.requireNonNull(uri);
Objects.requireNonNull(size);
- // Older apps might be relying on mutable results, so only consider
- // giving them hardware bitmaps once they target Q or higher. If modern
- // apps need mutable thumbnails, they can always roll their own logic.
- final int allocator;
- if (getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
- allocator = ImageDecoder.ALLOCATOR_DEFAULT;
- } else {
- allocator = ImageDecoder.ALLOCATOR_SOFTWARE;
- }
-
try (ContentProviderClient client = acquireContentProviderClient(uri)) {
- return loadThumbnail(client, uri, size, signal, allocator);
+ return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_SOFTWARE);
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 004417b..cec8ef5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -425,6 +425,15 @@
*/
public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
+ /**
+ * These bind flags reduce the strength of the binding such that we shouldn't
+ * consider it as pulling the process up to the level of the one that is bound to it.
+ * @hide
+ */
+ public static final int BIND_REDUCTION_FLAGS =
+ Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_WAIVE_PRIORITY
+ | Context.BIND_ADJUST_BELOW_PERCEPTIBLE | Context.BIND_NOT_VISIBLE;
+
/** @hide */
@IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
RECEIVER_VISIBLE_TO_INSTANT_APPS
@@ -2982,6 +2991,31 @@
}
/**
+ * For a service previously bound with {@link #bindService} or a related method, change
+ * how the system manages that service's process in relation to other processes. This
+ * doesn't modify the original bind flags that were passed in when binding, but adjusts
+ * how the process will be managed in some cases based on those flags. Currently only
+ * works on isolated processes (will be ignored for non-isolated processes).
+ *
+ * @param conn The connection interface previously supplied to bindService(). This
+ * parameter must not be null.
+ * @param group A group to put this connection's process in. Upon calling here, this
+ * will override any previous group that was set for that process. The group
+ * tells the system about processes that are logically grouped together, so
+ * should be managed as one unit of importance (such as when being considered
+ * a recently used app). All processes in the same app with the same group
+ * are considered to be related. Supplying 0 reverts to the default behavior
+ * of not grouping.
+ * @param importance Additional importance of the processes within a group. Upon calling
+ * here, this will override any previous group that was set for that
+ * process. This fine-tunes process killing of all processes within
+ * a related groups -- higher importance values will be killed before
+ * lower ones.
+ */
+ public abstract void updateServiceGroup(@NonNull ServiceConnection conn, int group,
+ int importance);
+
+ /**
* Disconnect from an application service. You will no longer receive
* calls as the service is restarted, and the service is now allowed to
* stop at any time.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 88696b0e..2db44b4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -726,6 +726,11 @@
}
@Override
+ public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+ mBase.updateServiceGroup(conn, group, importance);
+ }
+
+ @Override
public void unbindService(ServiceConnection conn) {
mBase.unbindService(conn);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 10c6bc6..6fd5061 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1767,6 +1767,9 @@
* 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>
*
@@ -3586,6 +3589,27 @@
public static final String
ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
+
+ /**
+ * Activity Action: Perform text translation.
+ * <p>
+ * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to translate.
+ * <p>
+ * Output: nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
+
+ /**
+ * Activity Action: Define the meaning of the selected word(s).
+ * <p>
+ * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to define.
+ * <p>
+ * Output: nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_DEFINE = "android.intent.action.DEFINE";
+
/**
* Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or
* exisiting sensor being disconnected.
@@ -4092,6 +4116,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 48240db..7c3b5e4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -32,7 +32,6 @@
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.text.TextUtils;
import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -45,6 +44,7 @@
import java.text.Collator;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.List;
import java.util.Objects;
import java.util.UUID;
@@ -463,16 +463,6 @@
public static final int PRIVATE_FLAG_CANT_SAVE_STATE = 1<<1;
/**
- * Value for {@link #privateFlags}: Set to true if the application has been
- * installed using the forward lock option.
- *
- * NOTE: DO NOT CHANGE THIS VALUE! It is saved in packages.xml.
- *
- * {@hide}
- */
- public static final int PRIVATE_FLAG_FORWARD_LOCK = 1<<2;
-
- /**
* Value for {@link #privateFlags}: set to {@code true} if the application
* is permitted to hold privileged permissions.
*
@@ -650,7 +640,6 @@
PRIVATE_FLAG_CANT_SAVE_STATE,
PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
PRIVATE_FLAG_DIRECT_BOOT_AWARE,
- PRIVATE_FLAG_FORWARD_LOCK,
PRIVATE_FLAG_HAS_DOMAIN_URLS,
PRIVATE_FLAG_HIDDEN,
PRIVATE_FLAG_INSTANT,
@@ -830,7 +819,17 @@
* the structure.
*/
public String[] sharedLibraryFiles;
-
+
+ /**
+ * List of all shared libraries this application is linked against. This
+ * field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
+ * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving
+ * the structure.
+ *
+ * {@hide}
+ */
+ public List<SharedLibraryInfo> sharedLibraryInfos;
+
/**
* Full path to the default directory assigned to the package for its
* persistent data.
@@ -1474,6 +1473,7 @@
seInfo = orig.seInfo;
seInfoUser = orig.seInfoUser;
sharedLibraryFiles = orig.sharedLibraryFiles;
+ sharedLibraryInfos = orig.sharedLibraryInfos;
dataDir = orig.dataDir;
deviceProtectedDataDir = orig.deviceProtectedDataDir;
credentialProtectedDataDir = orig.credentialProtectedDataDir;
@@ -1549,6 +1549,7 @@
dest.writeString(seInfo);
dest.writeString(seInfoUser);
dest.writeStringArray(sharedLibraryFiles);
+ dest.writeTypedList(sharedLibraryInfos);
dest.writeString(dataDir);
dest.writeString(deviceProtectedDataDir);
dest.writeString(credentialProtectedDataDir);
@@ -1621,6 +1622,7 @@
seInfo = source.readString();
seInfoUser = source.readString();
sharedLibraryFiles = source.readStringArray();
+ sharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR);
dataDir = source.readString();
deviceProtectedDataDir = source.readString();
credentialProtectedDataDir = source.readString();
@@ -1829,17 +1831,6 @@
return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
- /** @hide */
- public boolean isExternalAsec() {
- return TextUtils.isEmpty(volumeUuid) && isExternal();
- }
-
- /** @hide */
- @UnsupportedAppUsage
- public boolean isForwardLocked() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
- }
-
/**
* True if the application is installed as an instant app.
* @hide
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/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8f90199..07672d9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1374,12 +1374,6 @@
}
/** {@hide} */
- public void setInstallFlagsInternal() {
- installFlags |= PackageManager.INSTALL_INTERNAL;
- installFlags &= ~PackageManager.INSTALL_EXTERNAL;
- }
-
- /** {@hide} */
@SystemApi
public void setAllowDowngrade(boolean allowDowngrade) {
if (allowDowngrade) {
@@ -1390,12 +1384,6 @@
}
/** {@hide} */
- public void setInstallFlagsExternal() {
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- installFlags &= ~PackageManager.INSTALL_INTERNAL;
- }
-
- /** {@hide} */
public void setInstallFlagsForcePermissionPrompt() {
installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 361beba..6421dc5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -700,10 +700,8 @@
/** @hide */
@IntDef(flag = true, prefix = { "INSTALL_" }, value = {
- INSTALL_FORWARD_LOCK,
INSTALL_REPLACE_EXISTING,
INSTALL_ALLOW_TEST,
- INSTALL_EXTERNAL,
INSTALL_INTERNAL,
INSTALL_FROM_ADB,
INSTALL_ALL_USERS,
@@ -721,17 +719,6 @@
public @interface InstallFlags {}
/**
- * Flag parameter for {@link #installPackage} to indicate that this package
- * should be installed as forward locked, i.e. only the app itself should
- * have access to its code and non-resource assets.
- *
- * @deprecated new installs into ASEC containers are no longer supported.
- * @hide
- */
- @Deprecated
- public static final int INSTALL_FORWARD_LOCK = 0x00000001;
-
- /**
* Flag parameter for {@link #installPackage} to indicate that you want to
* replace an already installed package, if one exists.
*
@@ -750,17 +737,6 @@
/**
* Flag parameter for {@link #installPackage} to indicate that this package
- * must be installed to an ASEC on a {@link VolumeInfo#TYPE_PUBLIC}.
- *
- * @deprecated new installs into ASEC containers are no longer supported;
- * use adoptable storage instead.
- * @hide
- */
- @Deprecated
- public static final int INSTALL_EXTERNAL = 0x00000008;
-
- /**
- * Flag parameter for {@link #installPackage} to indicate that this package
* must be installed to internal storage.
*
* @hide
@@ -1521,14 +1497,6 @@
/**
* Error code that is passed to the {@link IPackageMoveObserver} if the
- * specified package cannot be moved since its forward locked.
- *
- * @hide
- */
- public static final int MOVE_FAILED_FORWARD_LOCKED = -4;
-
- /**
- * Error code that is passed to the {@link IPackageMoveObserver} if the
* specified package cannot be moved to the specified location.
*
* @hide
@@ -6441,4 +6409,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/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 7ef5264..49189e5 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);
}
}
}
@@ -829,9 +834,6 @@
public static final int PARSE_MUST_BE_APK = 1 << 0;
public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
- /** @deprecated forward lock no longer functional. remove. */
- @Deprecated
- public static final int PARSE_FORWARD_LOCK = 1 << 2;
public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
@@ -845,7 +847,6 @@
PARSE_ENFORCE_CODE,
PARSE_EXTERNAL_STORAGE,
PARSE_FORCE_SDK,
- PARSE_FORWARD_LOCK,
PARSE_IGNORE_PROCESSES,
PARSE_IS_SYSTEM_DIR,
PARSE_MUST_BE_APK,
@@ -2006,11 +2007,6 @@
PARSE_DEFAULT_TARGET_SANDBOX);
pkg.applicationInfo.targetSandboxVersion = targetSandboxVersion;
- /* Set the global "forward lock" flag */
- if ((flags & PARSE_FORWARD_LOCK) != 0) {
- pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
- }
-
/* Set the global "on SD card" flag */
if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
@@ -2123,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)) {
@@ -2451,7 +2447,7 @@
newPermsMsg.append(' ');
}
newPermsMsg.append(npi.name);
- pkg.requestedPermissions.add(npi.name);
+ addRequestedPermission(pkg, npi.name);
pkg.implicitPermissions.add(npi.name);
}
}
@@ -2472,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);
}
}
@@ -2532,55 +2528,33 @@
final ArraySet<String> newPermissions = new ArraySet<>();
newPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
- newPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
newPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
- newPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
newPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
- newPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
newPermissions.add(android.Manifest.permission.ACCESS_MEDIA_LOCATION);
newPermissions.add(android.Manifest.permission.WRITE_OBB);
- final ArraySet<String> dangerousPermissions = new ArraySet<>();
- dangerousPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE);
- dangerousPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ final ArraySet<String> removedPermissions = new ArraySet<>();
+ removedPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE);
+ removedPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
for (int i = pkg.permissions.size() - 1; i >= 0; i--) {
final Permission p = pkg.permissions.get(i);
if (newPermissions.contains(p.info.name)) {
pkg.permissions.remove(i);
- } else if (dangerousPermissions.contains(p.info.name)) {
- p.info.protectionLevel &= ~PermissionInfo.PROTECTION_MASK_BASE;
- p.info.protectionLevel |= PermissionInfo.PROTECTION_DANGEROUS;
+ } else if (removedPermissions.contains(p.info.name)) {
+ p.info.flags &= ~PermissionInfo.FLAG_REMOVED;
}
}
}
} else {
if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_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);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_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);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
- }
-
- if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_LEGACY, false)) {
- if (pkg.requestedPermissions
- .contains(android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
- }
- if (pkg.requestedPermissions
- .contains(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
- }
+ addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_IMAGES);
}
}
@@ -2620,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.
@@ -2876,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);
@@ -2901,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);
@@ -2933,6 +2953,10 @@
+ parser.getPositionDescription());
}
+ UsesPermissionInfo info = new UsesPermissionInfo(name, dataSentOffDevice,
+ dataSharedWithThirdParty, dataUsedForMonetization, retention, retentionWeeks);
+ pkg.usesPermissionInfos.add(info);
+
return true;
}
@@ -3267,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) {
@@ -5369,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;
@@ -6396,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<>();
@@ -6801,7 +6837,7 @@
/** @hide */
public boolean isForwardLocked() {
- return applicationInfo.isForwardLocked();
+ return false;
}
/** @hide */
@@ -6843,9 +6879,7 @@
public boolean canHaveOatDir() {
// The following app types CANNOT have oat directory
// - non-updated system apps
- // - forward-locked apps or apps installed in ASEC containers
- return (!isSystem() || isUpdatedSystemApp())
- && !isForwardLocked() && !applicationInfo.isExternalAsec();
+ return !isSystem() || isUpdatedSystemApp();
}
public boolean isMatch(int flags) {
@@ -6928,6 +6962,7 @@
dest.readStringList(requestedPermissions);
internStringArrayList(requestedPermissions);
+ dest.readParcelableList(usesPermissionInfos, boot);
dest.readStringList(implicitPermissions);
internStringArrayList(implicitPermissions);
protectedBroadcasts = dest.createStringArrayList();
@@ -7094,6 +7129,7 @@
dest.writeParcelableList(instrumentation, flags);
dest.writeStringList(requestedPermissions);
+ dest.writeParcelableList(usesPermissionInfos, flags);
dest.writeStringList(implicitPermissions);
dest.writeStringList(protectedBroadcasts);
@@ -7669,6 +7705,7 @@
}
if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
ai.sharedLibraryFiles = p.usesLibraryFiles;
+ ai.sharedLibraryInfos = p.usesLibraryInfos;
}
if (state.stopped) {
ai.flags |= ApplicationInfo.FLAG_STOPPED;
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 60c06a1..7523949 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/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 096301c..ad82626d 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -74,6 +74,7 @@
private final String mPath;
private final String mPackageName;
private final String mName;
+ private final List<String> mCodePaths;
private final long mVersion;
private final @Type int mType;
@@ -84,6 +85,8 @@
/**
* Creates a new instance.
*
+ * @param codePaths For a non {@link #TYPE_BUILTIN builtin} library, the locations of jars of
+ * this shared library. Null for builtin library.
* @param name The lib name.
* @param version The lib version if not builtin.
* @param type The lib type.
@@ -92,11 +95,13 @@
*
* @hide
*/
- public SharedLibraryInfo(String path, String packageName, String name, long version, int type,
+ public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
+ String name, long version, int type,
VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
List<SharedLibraryInfo> dependencies) {
mPath = path;
mPackageName = packageName;
+ mCodePaths = codePaths;
mName = name;
mVersion = version;
mType = type;
@@ -106,7 +111,8 @@
}
private SharedLibraryInfo(Parcel parcel) {
- this(parcel.readString(), parcel.readString(), parcel.readString(), parcel.readLong(),
+ this(parcel.readString(), parcel.readString(), parcel.readArrayList(null),
+ parcel.readString(), parcel.readLong(),
parcel.readInt(), parcel.readParcelable(null), parcel.readArrayList(null),
parcel.createTypedArrayList(SharedLibraryInfo.CREATOR));
}
@@ -155,6 +161,25 @@
}
/**
+ * Get all code paths for that library.
+ *
+ * @return All code paths.
+ *
+ * @hide
+ */
+ public List<String> getAllCodePaths() {
+ if (getPath() != null) {
+ // Builtin library.
+ ArrayList<String> list = new ArrayList<>();
+ list.add(getPath());
+ return list;
+ } else {
+ // Static or dynamic library.
+ return mCodePaths;
+ }
+ }
+
+ /**
* Add a library dependency to that library. Note that this
* should be called under the package manager lock.
*
@@ -273,6 +298,7 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mPath);
parcel.writeString(mPackageName);
+ parcel.writeList(mCodePaths);
parcel.writeString(mName);
parcel.writeLong(mVersion);
parcel.writeInt(mType);
diff --git a/core/java/android/content/pm/UsesPermissionInfo.java b/core/java/android/content/pm/UsesPermissionInfo.java
new file mode 100644
index 0000000..997552b
--- /dev/null
+++ b/core/java/android/content/pm/UsesPermissionInfo.java
@@ -0,0 +1,274 @@
+/*
+ * 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 #flags}: 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.attrs#dataSentOffDevice}, {@link android.R.attrs#dataSharedWithThirdParty},
+ * and {@link android.R.attrs#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.attrs#dataSentOffDevice}, {@link android.R.attrs#dataSharedWithThirdParty},
+ * and {@link android.R.attrs#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.attrs#dataSentOffDevice}, {@link android.R.attrs#dataSharedWithThirdParty},
+ * and {@link android.R.attrs#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.attrs#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.attrs#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.attrs#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.attrs#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.attrs#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.attrs#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.attrs#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.attrs#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/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/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/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/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 8599f47..3552655 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -65,10 +65,13 @@
// An interval, in seconds between the NattKeepalive packets
private int mNattKeepaliveInterval;
- // XFRM mark and mask
+ // XFRM mark and mask; defaults to 0 (no mark/mask)
private int mMarkValue;
private int mMarkMask;
+ // XFRM interface id
+ private int mXfrmInterfaceId;
+
/** Set the mode for this IPsec transform */
public void setMode(int mode) {
mMode = mode;
@@ -125,14 +128,30 @@
mNattKeepaliveInterval = interval;
}
+ /**
+ * Sets the mark value
+ *
+ * <p>Internal (System server) use only. Marks passed in by users will be overwritten or
+ * ignored.
+ */
public void setMarkValue(int mark) {
mMarkValue = mark;
}
+ /**
+ * Sets the mark mask
+ *
+ * <p>Internal (System server) use only. Marks passed in by users will be overwritten or
+ * ignored.
+ */
public void setMarkMask(int mask) {
mMarkMask = mask;
}
+ public void setXfrmInterfaceId(int xfrmInterfaceId) {
+ mXfrmInterfaceId = xfrmInterfaceId;
+ }
+
// Transport or Tunnel
public int getMode() {
return mMode;
@@ -190,6 +209,10 @@
return mMarkMask;
}
+ public int getXfrmInterfaceId() {
+ return mXfrmInterfaceId;
+ }
+
// Parcelable Methods
@Override
@@ -213,6 +236,7 @@
out.writeInt(mNattKeepaliveInterval);
out.writeInt(mMarkValue);
out.writeInt(mMarkMask);
+ out.writeInt(mXfrmInterfaceId);
}
@VisibleForTesting
@@ -235,6 +259,7 @@
mNattKeepaliveInterval = c.mNattKeepaliveInterval;
mMarkValue = c.mMarkValue;
mMarkMask = c.mMarkMask;
+ mXfrmInterfaceId = c.mXfrmInterfaceId;
}
private IpSecConfig(Parcel in) {
@@ -255,6 +280,7 @@
mNattKeepaliveInterval = in.readInt();
mMarkValue = in.readInt();
mMarkMask = in.readInt();
+ mXfrmInterfaceId = in.readInt();
}
@Override
@@ -289,6 +315,8 @@
.append(mMarkValue)
.append(", mMarkMask=")
.append(mMarkMask)
+ .append(", mXfrmInterfaceId=")
+ .append(mXfrmInterfaceId)
.append("}");
return strBuilder.toString();
@@ -320,10 +348,10 @@
&& lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
&& lhs.mSpiResourceId == rhs.mSpiResourceId
&& IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
- && IpSecAlgorithm.equals(
- lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
+ && IpSecAlgorithm.equals(lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
&& IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication)
&& lhs.mMarkValue == rhs.mMarkValue
- && lhs.mMarkMask == rhs.mMarkMask);
+ && lhs.mMarkMask == rhs.mMarkMask
+ && lhs.mXfrmInterfaceId == rhs.mXfrmInterfaceId);
}
}
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..1468fe5 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
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 720c167..97d72f0 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -360,6 +360,16 @@
}
/**
+ * Returns the number of binder proxies held in this process.
+ * @return number of binder proxies in this process
+ */
+ public static int getProxyCount() {
+ synchronized (sProxyMap) {
+ return sProxyMap.size();
+ }
+ }
+
+ /**
* Dump proxy debug information.
*
* @hide
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index 7858c59..767c15c 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -83,6 +83,14 @@
= "android.intent.action.UPDATE_SMART_SELECTION";
/**
+ * Update conversation actions model file.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_UPDATE_CONVERSATION_ACTIONS
+ = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
+
+ /**
* Update network watchlist config file.
* @hide
*/
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8c5c415..900b62d 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -54,7 +54,7 @@
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
- private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
+ private static final String ANGLE_PACKAGE_NAME = "com.google.android.angle";
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 287a5ed..8160338 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.IThermalEventListener;
+import android.os.IThermalStatusListener;
import android.os.Temperature;
import java.util.List;
@@ -30,31 +31,60 @@
* @param listener the IThermalEventListener to be notified.
* {@hide}
*/
- void registerThermalEventListener(in IThermalEventListener listener);
+ boolean registerThermalEventListener(in IThermalEventListener listener);
+
/**
* Register a listener for thermal events on given temperature type.
* @param listener the IThermalEventListener to be notified.
* @param type the temperature type IThermalEventListener to be notified.
+ * @return true if registered successfully.
* {@hide}
*/
- void registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
+ boolean registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
+
/**
* Unregister a previously-registered listener for thermal events.
* @param listener the IThermalEventListener to no longer be notified.
+ * @return true if unregistered successfully.
* {@hide}
*/
- void unregisterThermalEventListener(in IThermalEventListener listener);
+ boolean unregisterThermalEventListener(in IThermalEventListener listener);
+
/**
* Get current temperature with its throttling status.
* @return list of android.os.Temperature
* {@hide}
*/
List<Temperature> getCurrentTemperatures();
+
/**
* Get current temperature with its throttling status on given temperature type.
* @param type the temperature type to query.
- * @return list of android.os.Temperature
+ * @return list of {@link android.os.Temperature}.
* {@hide}
*/
List<Temperature> getCurrentTemperaturesWithType(in int type);
+
+ /**
+ * Register a listener for thermal status change.
+ * @param listener the IThermalStatusListener to be notified.
+ * @return true if registered successfully.
+ * {@hide}
+ */
+ boolean registerThermalStatusListener(in IThermalStatusListener listener);
+
+ /**
+ * Unregister a previously-registered listener for thermal status.
+ * @param listener the IThermalStatusListener to no longer be notified.
+ * @return true if unregistered successfully.
+ * {@hide}
+ */
+ boolean unregisterThermalStatusListener(in IThermalStatusListener listener);
+
+ /**
+ * Get current thermal status.
+ * @return status defined in {@link android.os.Temperature}.
+ * {@hide}
+ */
+ int getCurrentStatus();
}
diff --git a/core/java/android/os/IThermalStatusListener.aidl b/core/java/android/os/IThermalStatusListener.aidl
new file mode 100644
index 0000000..a6da7d0
--- /dev/null
+++ b/core/java/android/os/IThermalStatusListener.aidl
@@ -0,0 +1,29 @@
+/*
+** 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.os;
+
+/**
+ * Listener for thermal status.
+ * {@hide}
+ */
+oneway interface IThermalStatusListener {
+ /**
+ * Called when overall thermal throttling status changed.
+ * @param status defined in {@link android.os.Temperature#ThrottlingStatus}.
+ */
+ void onStatusChange(int status);
+}
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/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 37ed52c..bf85fbd 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -25,9 +25,7 @@
/**
* Temperature values used by IThermalService.
- */
-
-/**
+ *
* @hide
*/
public class Temperature implements Parcelable {
@@ -40,7 +38,6 @@
/** The level of the sensor is currently in throttling */
private int mStatus;
- /** @hide */
@IntDef(prefix = { "THROTTLING_" }, value = {
THROTTLING_NONE,
THROTTLING_LIGHT,
@@ -62,7 +59,6 @@
public static final int THROTTLING_WARNING = ThrottlingSeverity.WARNING;
public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN;
- /** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_UNKNOWN,
TYPE_CPU,
@@ -95,19 +91,28 @@
*
* @return true if a temperature type is valid otherwise false.
*/
- public static boolean isValidType(int type) {
+ public static boolean isValidType(@Type int type) {
return type >= TYPE_UNKNOWN && type <= TYPE_BCL_PERCENTAGE;
}
+ /**
+ * Verify a valid throttling status.
+ *
+ * @return true if a status is valid otherwise false.
+ */
+ public static boolean isValidStatus(@ThrottlingStatus int status) {
+ return status >= THROTTLING_NONE && status <= THROTTLING_SHUTDOWN;
+ }
+
public Temperature() {
this(Float.NaN, TYPE_UNKNOWN, "", THROTTLING_NONE);
}
- public Temperature(float value, @Type int type, String name, int status) {
+ public Temperature(float value, @Type int type, String name, @ThrottlingStatus int status) {
mValue = value;
mType = isValidType(type) ? type : TYPE_UNKNOWN;
mName = name;
- mStatus = status;
+ mStatus = isValidStatus(status) ? status : THROTTLING_NONE;
}
/**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 423ce77..b42f1c4 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -137,8 +137,6 @@
public static final String PROP_FORCE_VIDEO = "persist.fw.force_video";
/** {@hide} */
public static final String PROP_FORCE_IMAGES = "persist.fw.force_images";
- /** {@hide} */
- public static final String PROP_FORCE_LEGACY = "persist.fw.force_legacy";
/** {@hide} */
public static final String UUID_PRIVATE_INTERNAL = null;
diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java
index fee3f0f1..948c6aa 100644
--- a/core/java/android/preference/CheckBoxPreference.java
+++ b/core/java/android/preference/CheckBoxPreference.java
@@ -27,11 +27,18 @@
* functionality.
* <p>
* This preference will store a boolean into the SharedPreferences.
- *
+ *
* @attr ref android.R.styleable#CheckBoxPreference_summaryOff
* @attr ref android.R.styleable#CheckBoxPreference_summaryOn
* @attr ref android.R.styleable#CheckBoxPreference_disableDependentsState
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class CheckBoxPreference extends TwoStatePreference {
public CheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr) {
diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index 4b5a7b4..96c8589 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -50,7 +50,14 @@
* @attr ref android.R.styleable#DialogPreference_dialogLayout
* @attr ref android.R.styleable#DialogPreference_positiveButtonText
* @attr ref android.R.styleable#DialogPreference_negativeButtonText
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public abstract class DialogPreference extends Preference implements
DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
PreferenceManager.OnActivityDestroyListener {
diff --git a/core/java/android/preference/EditTextPreference.java b/core/java/android/preference/EditTextPreference.java
index 4d2ac67..c09cec8 100644
--- a/core/java/android/preference/EditTextPreference.java
+++ b/core/java/android/preference/EditTextPreference.java
@@ -42,7 +42,14 @@
* This preference will store a string into the SharedPreferences.
* <p>
* See {@link android.R.styleable#EditText EditText Attributes}.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class EditTextPreference extends DialogPreference {
/**
* The edit text shown in the dialog.
diff --git a/core/java/android/preference/GenericInflater.java b/core/java/android/preference/GenericInflater.java
index 3319e64..7edc987 100644
--- a/core/java/android/preference/GenericInflater.java
+++ b/core/java/android/preference/GenericInflater.java
@@ -16,13 +16,6 @@
package android.preference;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.util.HashMap;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.annotation.XmlRes;
import android.content.Context;
import android.content.res.XmlResourceParser;
@@ -32,6 +25,13 @@
import android.view.InflateException;
import android.view.LayoutInflater;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
// TODO: fix generics
/**
* Generic XML inflater. This has been adapted from {@link LayoutInflater} and
@@ -41,7 +41,14 @@
* @param T The type of the items to inflate
* @param P The type of parents (that is those items that contain other items).
* Must implement {@link GenericInflater.Parent}
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
abstract class GenericInflater<T, P extends GenericInflater.Parent> {
private final boolean DEBUG = false;
diff --git a/core/java/android/preference/ListPreference.java b/core/java/android/preference/ListPreference.java
index c0c71af..14c1dc81 100644
--- a/core/java/android/preference/ListPreference.java
+++ b/core/java/android/preference/ListPreference.java
@@ -36,7 +36,14 @@
*
* @attr ref android.R.styleable#ListPreference_entries
* @attr ref android.R.styleable#ListPreference_entryValues
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class ListPreference extends DialogPreference {
private CharSequence[] mEntries;
private CharSequence[] mEntryValues;
diff --git a/core/java/android/preference/MultiCheckPreference.java b/core/java/android/preference/MultiCheckPreference.java
index c1260a4..e3d0e26 100644
--- a/core/java/android/preference/MultiCheckPreference.java
+++ b/core/java/android/preference/MultiCheckPreference.java
@@ -16,8 +16,6 @@
package android.preference;
-import java.util.Arrays;
-
import android.annotation.ArrayRes;
import android.app.AlertDialog.Builder;
import android.content.Context;
@@ -27,6 +25,8 @@
import android.os.Parcelable;
import android.util.AttributeSet;
+import java.util.Arrays;
+
/**
* @hide
* A {@link Preference} that displays a list of entries as
@@ -34,7 +34,14 @@
*
* @attr ref android.R.styleable#ListPreference_entries
* @attr ref android.R.styleable#ListPreference_entryValues
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class MultiCheckPreference extends DialogPreference {
private CharSequence[] mEntries;
private String[] mEntryValues;
diff --git a/core/java/android/preference/MultiSelectListPreference.java b/core/java/android/preference/MultiSelectListPreference.java
index 138bd878..43182d9 100644
--- a/core/java/android/preference/MultiSelectListPreference.java
+++ b/core/java/android/preference/MultiSelectListPreference.java
@@ -35,10 +35,17 @@
* This preference will store a set of strings into the SharedPreferences.
* This set will contain one or more values from the
* {@link #setEntryValues(CharSequence[])} array.
- *
+ *
* @attr ref android.R.styleable#MultiSelectListPreference_entries
* @attr ref android.R.styleable#MultiSelectListPreference_entryValues
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class MultiSelectListPreference extends DialogPreference {
private CharSequence[] mEntries;
private CharSequence[] mEntryValues;
@@ -65,25 +72,25 @@
public MultiSelectListPreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
}
-
+
public MultiSelectListPreference(Context context) {
this(context, null);
}
-
+
/**
* Sets the human-readable entries to be shown in the list. This will be
* shown in subsequent dialogs.
* <p>
* Each entry must have a corresponding index in
* {@link #setEntryValues(CharSequence[])}.
- *
+ *
* @param entries The entries.
* @see #setEntryValues(CharSequence[])
*/
public void setEntries(CharSequence[] entries) {
mEntries = entries;
}
-
+
/**
* @see #setEntries(CharSequence[])
* @param entriesResId The entries array as a resource.
@@ -91,21 +98,21 @@
public void setEntries(@ArrayRes int entriesResId) {
setEntries(getContext().getResources().getTextArray(entriesResId));
}
-
+
/**
* The list of entries to be shown in the list in subsequent dialogs.
- *
+ *
* @return The list as an array.
*/
public CharSequence[] getEntries() {
return mEntries;
}
-
+
/**
* The array to find the value to save for a preference when an entry from
* entries is selected. If a user clicks on the second item in entries, the
* second item in this array will be saved to the preference.
- *
+ *
* @param entryValues The array to be used as values to save for the preference.
*/
public void setEntryValues(CharSequence[] entryValues) {
@@ -119,20 +126,20 @@
public void setEntryValues(@ArrayRes int entryValuesResId) {
setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
}
-
+
/**
* Returns the array of values to be saved for the preference.
- *
+ *
* @return The array of values.
*/
public CharSequence[] getEntryValues() {
return mEntryValues;
}
-
+
/**
* Sets the value of the key. This should contain entries in
* {@link #getEntryValues()}.
- *
+ *
* @param values The values to set for the key.
*/
public void setValues(Set<String> values) {
@@ -141,17 +148,17 @@
persistStringSet(values);
}
-
+
/**
* Retrieves the current value of the key.
*/
public Set<String> getValues() {
return mValues;
}
-
+
/**
* Returns the index of the given value (in the entry values array).
- *
+ *
* @param value The value whose index should be returned.
* @return The index of the value, or -1 if not found.
*/
@@ -165,17 +172,17 @@
}
return -1;
}
-
+
@Override
protected void onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
-
+
if (mEntries == null || mEntryValues == null) {
throw new IllegalStateException(
"MultiSelectListPreference requires an entries array and " +
"an entryValues array.");
}
-
+
boolean[] checkedItems = getSelectedItems();
builder.setMultiChoiceItems(mEntries, checkedItems,
new DialogInterface.OnMultiChoiceClickListener() {
@@ -190,24 +197,24 @@
mNewValues.clear();
mNewValues.addAll(mValues);
}
-
+
private boolean[] getSelectedItems() {
final CharSequence[] entries = mEntryValues;
final int entryCount = entries.length;
final Set<String> values = mValues;
boolean[] result = new boolean[entryCount];
-
+
for (int i = 0; i < entryCount; i++) {
result[i] = values.contains(entries[i].toString());
}
-
+
return result;
}
-
+
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
-
+
if (positiveResult && mPreferenceChanged) {
final Set<String> values = mNewValues;
if (callChangeListener(values)) {
@@ -216,25 +223,25 @@
}
mPreferenceChanged = false;
}
-
+
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
final CharSequence[] defaultValues = a.getTextArray(index);
final int valueCount = defaultValues.length;
final Set<String> result = new HashSet<String>();
-
+
for (int i = 0; i < valueCount; i++) {
result.add(defaultValues[i].toString());
}
-
+
return result;
}
-
+
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValues(restoreValue ? getPersistedStringSet(mValues) : (Set<String>) defaultValue);
}
-
+
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
@@ -242,42 +249,42 @@
// No need to save instance state
return superState;
}
-
+
final SavedState myState = new SavedState(superState);
myState.values = getValues();
return myState;
}
-
+
private static class SavedState extends BaseSavedState {
Set<String> values;
-
+
public SavedState(Parcel source) {
super(source);
values = new HashSet<String>();
String[] strings = source.readStringArray();
-
+
final int stringCount = strings.length;
for (int i = 0; i < stringCount; i++) {
values.add(strings[i]);
}
}
-
+
public SavedState(Parcelable superState) {
super(superState);
}
-
+
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeStringArray(values.toArray(new String[0]));
}
-
+
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
-
+
public SavedState[] newArray(int size) {
return new SavedState[size];
}
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 2387657..3c1ba9d 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -86,7 +86,14 @@
* @attr ref android.R.styleable#Preference_recycleEnabled
* @attr ref android.R.styleable#Preference_singleLineTitle
* @attr ref android.R.styleable#Preference_iconSpaceReserved
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class Preference implements Comparable<Preference> {
/**
* Specify for {@link #setOrder(int)} if a specific order is not required.
@@ -165,7 +172,14 @@
* {@link Preference} has been changed by the user and is
* about to be set and/or persisted. This gives the client a chance
* to prevent setting and/or persisting the value.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnPreferenceChangeListener {
/**
* Called when a Preference has been changed by the user. This is
@@ -182,7 +196,14 @@
/**
* Interface definition for a callback to be invoked when a {@link Preference} is
* clicked.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnPreferenceClickListener {
/**
* Called when a Preference has been clicked.
@@ -2070,7 +2091,14 @@
/**
* A base class for managing the instance state of a {@link Preference}.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public static class BaseSavedState extends AbsSavedState {
public BaseSavedState(Parcel source) {
super(source);
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 8ed2605..eab5937 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -97,34 +97,13 @@
* guide.</p>
* </div>
*
- * <a name="SampleCode"></a>
- * <h3>Sample Code</h3>
- *
- * <p>The following sample code shows a simple preference activity that
- * has two different sets of preferences. The implementation, consisting
- * of the activity itself as well as its two preference fragments is:</p>
- *
- * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/PreferenceWithHeaders.java
- * activity}
- *
- * <p>The preference_headers resource describes the headers to be displayed
- * and the fragments associated with them. It is:
- *
- * {@sample development/samples/ApiDemos/res/xml/preference_headers.xml headers}
- *
- * <p>The first header is shown by Prefs1Fragment, which populates itself
- * from the following XML resource:</p>
- *
- * {@sample development/samples/ApiDemos/res/xml/fragmented_preferences.xml preferences}
- *
- * <p>Note that this XML resource contains a preference screen holding another
- * fragment, the Prefs1FragmentInner implemented here. This allows the user
- * to traverse down a hierarchy of preferences; pressing back will pop each
- * fragment off the stack to return to the previous preferences.
- *
- * <p>See {@link PreferenceFragment} for information on implementing the
- * fragments themselves.
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public abstract class PreferenceActivity extends ListActivity implements
PreferenceManager.OnPreferenceTreeClickListener,
PreferenceFragment.OnPreferenceStartFragmentCallback {
@@ -337,7 +316,14 @@
/**
* Description of a single Header item that the user can select.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public static final class Header implements Parcelable {
/**
* Identifier for this header, to correlate with a new list when
diff --git a/core/java/android/preference/PreferenceCategory.java b/core/java/android/preference/PreferenceCategory.java
index 253481b..887e468 100644
--- a/core/java/android/preference/PreferenceCategory.java
+++ b/core/java/android/preference/PreferenceCategory.java
@@ -29,7 +29,14 @@
* read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
* guide.</p>
* </div>
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class PreferenceCategory extends PreferenceGroup {
private static final String TAG = "PreferenceCategory";
diff --git a/core/java/android/preference/PreferenceDataStore.java b/core/java/android/preference/PreferenceDataStore.java
index 8caa404..5171632 100644
--- a/core/java/android/preference/PreferenceDataStore.java
+++ b/core/java/android/preference/PreferenceDataStore.java
@@ -39,7 +39,14 @@
*
* @see Preference#setPreferenceDataStore(PreferenceDataStore)
* @see PreferenceManager#setPreferenceDataStore(PreferenceDataStore)
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public interface PreferenceDataStore {
/**
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 548895e..d6c069f0 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -88,25 +88,14 @@
* guide.</p>
* </div>
*
- * <a name="SampleCode"></a>
- * <h3>Sample Code</h3>
- *
- * <p>The following sample code shows a simple preference fragment that is
- * populated from a resource. The resource it loads is:</p>
- *
- * {@sample development/samples/ApiDemos/res/xml/preferences.xml preferences}
- *
- * <p>The fragment implementation itself simply populates the preferences
- * when created. Note that the preferences framework takes care of loading
- * the current values out of the app preferences and writing them when changed:</p>
- *
- * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/FragmentPreferences.java
- * fragment}
- *
* @see Preference
* @see PreferenceScreen
*
- * @deprecated Use {@link android.support.v7.preference.PreferenceFragmentCompat}
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
@Deprecated
public abstract class PreferenceFragment extends Fragment implements
diff --git a/core/java/android/preference/PreferenceFrameLayout.java b/core/java/android/preference/PreferenceFrameLayout.java
index 886338f..c667824 100644
--- a/core/java/android/preference/PreferenceFrameLayout.java
+++ b/core/java/android/preference/PreferenceFrameLayout.java
@@ -24,7 +24,14 @@
/**
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class PreferenceFrameLayout extends FrameLayout {
private static final int DEFAULT_BORDER_TOP = 0;
private static final int DEFAULT_BORDER_BOTTOM = 0;
diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java
index f135b26..b33ea4e 100644
--- a/core/java/android/preference/PreferenceGroup.java
+++ b/core/java/android/preference/PreferenceGroup.java
@@ -16,15 +16,16 @@
package android.preference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.AttributeSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* A container for multiple
* {@link Preference} objects. It is a base class for Preference objects that are
@@ -38,7 +39,14 @@
* </div>
*
* @attr ref android.R.styleable#PreferenceGroup_orderingFromXml
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public abstract class PreferenceGroup extends Preference implements GenericInflater.Parent<Preference> {
/**
* The container for child {@link Preference}s. This is sorted based on the
diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java
index bee45ab..fb41ea8 100644
--- a/core/java/android/preference/PreferenceGroupAdapter.java
+++ b/core/java/android/preference/PreferenceGroupAdapter.java
@@ -16,10 +16,6 @@
package android.preference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.preference.Preference.OnPreferenceChangeInternalListener;
@@ -30,6 +26,10 @@
import android.widget.FrameLayout;
import android.widget.ListView;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* An adapter that returns the {@link Preference} contained in this group.
* In most cases, this adapter should be the base class for any custom
@@ -49,7 +49,14 @@
* @see PreferenceCategoryAdapter
*
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class PreferenceGroupAdapter extends BaseAdapter
implements OnPreferenceChangeInternalListener {
diff --git a/core/java/android/preference/PreferenceInflater.java b/core/java/android/preference/PreferenceInflater.java
index 727fbca..04ad107 100644
--- a/core/java/android/preference/PreferenceInflater.java
+++ b/core/java/android/preference/PreferenceInflater.java
@@ -16,16 +16,16 @@
package android.preference;
-import com.android.internal.util.XmlUtils;
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
-import java.io.IOException;
+import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import android.content.Context;
-import android.content.Intent;
-import android.util.AttributeSet;
+import java.io.IOException;
/**
* The {@link PreferenceInflater} is used to inflate preference hierarchies from
@@ -34,7 +34,14 @@
* Do not construct this directly, instead use
* {@link Context#getSystemService(String)} with
* {@link Context#PREFERENCE_INFLATER_SERVICE}.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
class PreferenceInflater extends GenericInflater<Preference, PreferenceGroup> {
private static final String TAG = "PreferenceInflater";
private static final String INTENT_TAG_NAME = "intent";
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index dfee1af..f741bd6 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -47,7 +47,14 @@
* {@link PreferenceActivity#addPreferencesFromResource(int)}.
*
* @see PreferenceActivity
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class PreferenceManager {
private static final String TAG = "PreferenceManager";
@@ -1004,7 +1011,14 @@
* clicked.
*
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnPreferenceTreeClickListener {
/**
* Called when a preference in the tree rooted at this
@@ -1021,7 +1035,14 @@
/**
* Interface definition for a class that will be called when the container's activity
* receives an activity result.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnActivityResultListener {
/**
@@ -1036,7 +1057,14 @@
/**
* Interface definition for a class that will be called when the container's activity
* is stopped.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnActivityStopListener {
/**
@@ -1048,7 +1076,14 @@
/**
* Interface definition for a class that will be called when the container's activity
* is destroyed.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnActivityDestroyListener {
/**
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index dd00a53..c7653c8 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -86,7 +86,14 @@
* </div>
*
* @see PreferenceCategory
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public final class PreferenceScreen extends PreferenceGroup implements AdapterView.OnItemClickListener,
DialogInterface.OnDismissListener {
diff --git a/core/java/android/preference/RingtonePreference.java b/core/java/android/preference/RingtonePreference.java
index cd751cd..025aad0 100644
--- a/core/java/android/preference/RingtonePreference.java
+++ b/core/java/android/preference/RingtonePreference.java
@@ -40,7 +40,14 @@
* @attr ref android.R.styleable#RingtonePreference_ringtoneType
* @attr ref android.R.styleable#RingtonePreference_showDefault
* @attr ref android.R.styleable#RingtonePreference_showSilent
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class RingtonePreference extends Preference implements
PreferenceManager.OnActivityResultListener {
diff --git a/core/java/android/preference/SeekBarDialogPreference.java b/core/java/android/preference/SeekBarDialogPreference.java
index a8e5992..32ef821 100644
--- a/core/java/android/preference/SeekBarDialogPreference.java
+++ b/core/java/android/preference/SeekBarDialogPreference.java
@@ -28,7 +28,14 @@
/**
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class SeekBarDialogPreference extends DialogPreference {
private final Drawable mMyIcon;
diff --git a/core/java/android/preference/SeekBarPreference.java b/core/java/android/preference/SeekBarPreference.java
index cd35f3d..f789e31 100644
--- a/core/java/android/preference/SeekBarPreference.java
+++ b/core/java/android/preference/SeekBarPreference.java
@@ -29,7 +29,14 @@
/**
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class SeekBarPreference extends Preference
implements OnSeekBarChangeListener {
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index a871425..f01d5b1 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -45,7 +45,14 @@
/**
* Turns a {@link SeekBar} into a volume control.
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callback {
private static final String TAG = "SeekBarVolumizer";
diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java
index 1ec18bb..9dea1c8 100644
--- a/core/java/android/preference/SwitchPreference.java
+++ b/core/java/android/preference/SwitchPreference.java
@@ -36,7 +36,14 @@
* @attr ref android.R.styleable#SwitchPreference_switchTextOff
* @attr ref android.R.styleable#SwitchPreference_switchTextOn
* @attr ref android.R.styleable#SwitchPreference_disableDependentsState
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class SwitchPreference extends TwoStatePreference {
@UnsupportedAppUsage
private final Listener mListener = new Listener();
diff --git a/core/java/android/preference/TwoStatePreference.java b/core/java/android/preference/TwoStatePreference.java
index 2079a63..454472a 100644
--- a/core/java/android/preference/TwoStatePreference.java
+++ b/core/java/android/preference/TwoStatePreference.java
@@ -32,7 +32,14 @@
* Common base class for preferences that have two selectable states, persist a
* boolean value in SharedPreferences, and may have dependent preferences that are
* enabled/disabled based on the current state.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public abstract class TwoStatePreference extends Preference {
private CharSequence mSummaryOn;
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index ea1d1eb..92d848a 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -31,7 +31,14 @@
/**
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class VolumePreference extends SeekBarDialogPreference implements
PreferenceManager.OnActivityStopListener, View.OnKeyListener, SeekBarVolumizer.Callback {
@UnsupportedAppUsage
diff --git a/core/java/android/preference/package.html b/core/java/android/preference/package.html
index d24d5bb..382ed6e 100644
--- a/core/java/android/preference/package.html
+++ b/core/java/android/preference/package.html
@@ -1,23 +1,9 @@
<HTML>
<BODY>
-Provides classes that manage application preferences and implement the preferences UI.
-Using these ensures that all the preferences within each application are maintained
-in the same manner and the user experience is consistent with that of the system and
-other applications.
-<p>
-The preferences portion of an application
-should be ran as a separate {@link android.app.Activity} that extends
-the {@link android.preference.PreferenceActivity} class. In the PreferenceActivity, a
-{@link android.preference.PreferenceScreen} object should be the root element of the layout.
-The PreferenceScreen contains {@link android.preference.Preference} elements such as a
-{@link android.preference.CheckBoxPreference}, {@link android.preference.EditTextPreference},
-{@link android.preference.ListPreference}, {@link android.preference.PreferenceCategory},
-or {@link android.preference.RingtonePreference}. </p>
-<p>
-All settings made for a given {@link android.preference.Preference} will be automatically saved
-to the application's instance of {@link android.content.SharedPreferences}. Access to the
-SharedPreferences is simple with {@link android.preference.Preference#getSharedPreferences()}.</p>
-<p>
-Note that saved preferences are accessible only to the application that created them.</p>
+These classes are deprecated. Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+<a href="{@docRoot}reference/androidx/preference/package-summary.html">
+Preference Library</a> for consistent behavior across all devices. For more information on
+using the AndroidX Preference Library see
+<a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
</BODY>
</HTML>
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 16d454d..e032c18 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1232,7 +1232,7 @@
public static Bitmap getDocumentThumbnail(ContentProviderClient client, Uri documentUri,
Point size, CancellationSignal signal) throws IOException {
return ContentResolver.loadThumbnail(client, documentUri, Point.convert(size), signal,
- ImageDecoder.ALLOCATOR_DEFAULT);
+ ImageDecoder.ALLOCATOR_SOFTWARE);
}
/**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 291891e..e0e4fe2 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -572,6 +572,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 +791,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 +955,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 +974,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.
@@ -1671,18 +1779,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));
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b266648..d772903 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1692,6 +1692,15 @@
/** @hide - Private call() method to write to 'configuration' table */
public static final String CALL_METHOD_PUT_CONFIG = "PUT_config";
+ /** @hide - Private call() method to delete from the 'system' table */
+ public static final String CALL_METHOD_DELETE_SYSTEM = "DELETE_system";
+
+ /** @hide - Private call() method to delete from the 'secure' table */
+ public static final String CALL_METHOD_DELETE_SECURE = "DELETE_secure";
+
+ /** @hide - Private call() method to delete from the 'global' table */
+ public static final String CALL_METHOD_DELETE_GLOBAL = "DELETE_global";
+
/** @hide - Private call() method to reset to defaults the 'global' table */
public static final String CALL_METHOD_RESET_GLOBAL = "RESET_global";
@@ -1701,6 +1710,15 @@
/** @hide - Private call() method to reset to defaults the 'secure' table */
public static final String CALL_METHOD_RESET_SECURE = "RESET_secure";
+ /** @hide - Private call() method to query the 'system' table */
+ public static final String CALL_METHOD_LIST_SYSTEM = "LIST_system";
+
+ /** @hide - Private call() method to query the 'secure' table */
+ public static final String CALL_METHOD_LIST_SECURE = "LIST_secure";
+
+ /** @hide - Private call() method to query the 'global' table */
+ public static final String CALL_METHOD_LIST_GLOBAL = "LIST_global";
+
/**
* Activity Extra: Limit available options in launched activity based on the given authority.
* <p>
@@ -7949,6 +7967,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
@@ -9321,6 +9347,13 @@
"location_background_throttle_package_whitelist";
/**
+ * Whether to disable location status callbacks in preparation for deprecation.
+ * @hide
+ */
+ public static final String LOCATION_DISABLE_STATUS_CALLBACKS =
+ "location_disable_status_callbacks";
+
+ /**
* Maximum staleness allowed for last location when returned to clients with only foreground
* location permissions.
* @hide
@@ -10755,6 +10788,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
@@ -10975,6 +11043,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
*/
@@ -11540,7 +11618,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 =
@@ -12120,6 +12198,20 @@
"smart_selection_metadata_url";
/**
+ * URL for conversation actions model updates
+ * @hide
+ */
+ public static final String CONVERSATION_ACTIONS_UPDATE_CONTENT_URL =
+ "conversation_actions_content_url";
+
+ /**
+ * URL for conversation actions model update metadata
+ * @hide
+ */
+ public static final String CONVERSATION_ACTIONS_UPDATE_METADATA_URL =
+ "conversation_actions_metadata_url";
+
+ /**
* SELinux enforcement status. If 0, permissive; if 1, enforcing.
* @hide
*/
@@ -12153,7 +12245,7 @@
/**
* Defines global runtime overrides to window policy.
*
- * See {@link com.android.server.policy.PolicyControl} for value format.
+ * See {@link com.android.server.wm.PolicyControl} for value format.
*
* @hide
*/
@@ -12669,6 +12761,17 @@
public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets";
/**
+ * Used to emulate Smart Suggestion for Augmented Autofill during development
+ *
+ * <p>Valid values: {@code 0x1} for IME and/or {@code 0x2} for popup window.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS =
+ "autofill_smart_suggestion_emulation_flags";
+
+ /**
* Exemptions to the hidden API blacklist.
*
* @hide
@@ -13668,6 +13771,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/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index 09bba4b..e930f40 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.TelephonyManager;
import com.android.internal.telephony.uicc.IccUtils;
@@ -26,7 +27,10 @@
/**
* Used to pass info to CarrierConfigService implementations so they can decide what values to
- * return.
+ * return. Instead of passing mcc, mnc, gid1, gid2, spn, imsi to locate carrier information,
+ * CarrierIdentifier also include carrier id {@link TelephonyManager#getSimCarrierId()},
+ * a platform-wide unique identifier for each carrier. CarrierConfigService can directly use
+ * carrier id as the key to look up the carrier info.
*/
public class CarrierIdentifier implements Parcelable {
@@ -49,15 +53,40 @@
private @Nullable String mImsi;
private @Nullable String mGid1;
private @Nullable String mGid2;
+ private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+ private int mPreciseCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
public CarrierIdentifier(String mcc, String mnc, @Nullable String spn, @Nullable String imsi,
@Nullable String gid1, @Nullable String gid2) {
+ this(mcc, mnc, spn, imsi, gid1, gid2, TelephonyManager.UNKNOWN_CARRIER_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID);
+ }
+
+ /**
+ * @param mcc mobile country code
+ * @param mnc mobile network code
+ * @param spn service provider name
+ * @param imsi International Mobile Subscriber Identity {@link TelephonyManager#getSubscriberId()}
+ * @param gid1 group id level 1 {@link TelephonyManager#getGroupIdLevel1()}
+ * @param gid2 group id level 2
+ * @param carrierid carrier unique identifier {@link TelephonyManager#getSimCarrierId()}, used
+ * to uniquely identify the carrier and look up the carrier configurations.
+ * @param preciseCarrierId precise carrier identifier {@link TelephonyManager#getSimPreciseCarrierId()}
+ * @hide
+ *
+ * TODO: expose this to public API
+ */
+ public CarrierIdentifier(String mcc, String mnc, @Nullable String spn,
+ @Nullable String imsi, @Nullable String gid1, @Nullable String gid2,
+ int carrierid, int preciseCarrierId) {
mMcc = mcc;
mMnc = mnc;
mSpn = spn;
mImsi = imsi;
mGid1 = gid1;
mGid2 = gid2;
+ mCarrierId = carrierid;
+ mPreciseCarrierId = preciseCarrierId;
}
/**
@@ -125,6 +154,22 @@
return mGid2;
}
+ /**
+ * Get the carrier id {@link TelephonyManager#getSimCarrierId() }
+ * @hide
+ */
+ public int getCarrierId() {
+ return mCarrierId;
+ }
+
+ /**
+ * Get the precise carrier id {@link TelephonyManager#getSimPreciseCarrierId()}
+ * @hide
+ */
+ public int getPreciseCarrierId() {
+ return mPreciseCarrierId;
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -140,19 +185,14 @@
&& Objects.equals(mSpn, that.mSpn)
&& Objects.equals(mImsi, that.mImsi)
&& Objects.equals(mGid1, that.mGid1)
- && Objects.equals(mGid2, that.mGid2);
+ && Objects.equals(mGid2, that.mGid2)
+ && Objects.equals(mCarrierId, that.mCarrierId)
+ && Objects.equals(mPreciseCarrierId, that.mPreciseCarrierId);
}
@Override
- public int hashCode() {
- int result = 1;
- result = 31 * result + Objects.hashCode(mMcc);
- result = 31 * result + Objects.hashCode(mMnc);
- result = 31 * result + Objects.hashCode(mSpn);
- result = 31 * result + Objects.hashCode(mImsi);
- result = 31 * result + Objects.hashCode(mGid1);
- result = 31 * result + Objects.hashCode(mGid2);
- return result;
+ public int hashCode(){
+ return Objects.hash(mMcc, mMnc, mSpn, mImsi, mGid1, mGid2, mCarrierId, mPreciseCarrierId);
}
@Override
@@ -168,18 +208,22 @@
out.writeString(mImsi);
out.writeString(mGid1);
out.writeString(mGid2);
+ out.writeInt(mCarrierId);
+ out.writeInt(mPreciseCarrierId);
}
@Override
public String toString() {
return "CarrierIdentifier{"
- + "mcc=" + mMcc
- + ",mnc=" + mMnc
- + ",spn=" + mSpn
- + ",imsi=" + mImsi
- + ",gid1=" + mGid1
- + ",gid2=" + mGid2
- + "}";
+ + "mcc=" + mMcc
+ + ",mnc=" + mMnc
+ + ",spn=" + mSpn
+ + ",imsi=" + mImsi
+ + ",gid1=" + mGid1
+ + ",gid2=" + mGid2
+ + ",carrierid=" + mCarrierId
+ + ",mPreciseCarrierId=" + mPreciseCarrierId
+ + "}";
}
/** @hide */
@@ -190,6 +234,8 @@
mImsi = in.readString();
mGid1 = in.readString();
mGid2 = in.readString();
+ mCarrierId = in.readInt();
+ mPreciseCarrierId = in.readInt();
}
/** @hide */
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index b94ccf9..c351d89 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -93,7 +93,11 @@
* </p>
*
* @param id contains details about the current carrier that can be used do decide what
- * configuration values to return.
+ * configuration values to return. Instead of using details like MCCMNC to decide
+ * current carrier, it also contains subscription carrier id
+ * {@link android.telephony.TelephonyManager#getSimCarrierId()}, a platform-wide
+ * unique identifier for each carrier, CarrierConfigService can directly use carrier
+ * id as the key to look up the carrier info.
* @return a {@link PersistableBundle} object containing the configuration or null if default
* values should be used.
*/
diff --git a/core/java/android/service/intelligence/FillCallback.java b/core/java/android/service/intelligence/FillCallback.java
new file mode 100644
index 0000000..af2da79
--- /dev/null
+++ b/core/java/android/service/intelligence/FillCallback.java
@@ -0,0 +1,45 @@
+/*
+ * 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.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Callback used to indicate at {@link FillRequest} has been fulfilled.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillCallback {
+
+ FillCallback() {}
+
+ /**
+ * Sets the response associated with the request.
+ *
+ * @param response response associated with the request, or {@code null} if the service
+ * could not provide autofill for the request.
+ */
+ public void onSuccess(@Nullable FillResponse response) {
+ final FillWindow fillWindow = response.getFillWindow();
+ if (fillWindow != null) {
+ fillWindow.show();
+ }
+ // TODO(b/111330312): properly implement on server-side by updating the Session state
+ // accordingly (and adding CTS tests)
+ }
+}
diff --git a/core/java/android/service/intelligence/FillController.java b/core/java/android/service/intelligence/FillController.java
new file mode 100644
index 0000000..c5e1242
--- /dev/null
+++ b/core/java/android/service/intelligence/FillController.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,
+ * 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 static android.service.intelligence.IntelligenceService.DEBUG;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.util.Log;
+import android.util.Pair;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Object used to interact with the autofill system.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillController {
+ private static final String TAG = "FillController";
+
+ private final AutofillProxy mProxy;
+
+ FillController(@NonNull AutofillProxy proxy) {
+ mProxy = proxy;
+ }
+
+ /**
+ * Fills the activity with the provided values.
+ *
+ * <p>As a side effect, the {@link FillWindow} associated with the {@link FillResponse} will be
+ * automatically {@link FillWindow#destroy() destroyed}.
+ */
+ public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> values) {
+ Preconditions.checkNotNull(values);
+
+ if (DEBUG) {
+ Log.d(TAG, "autofill() with " + values.size() + " values");
+ }
+
+ try {
+ mProxy.autofill(values);
+ final FillWindow fillWindow = mProxy.getFillWindow();
+ if (fillWindow != null) {
+ fillWindow.destroy();
+ }
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/core/java/android/service/intelligence/FillRequest.java b/core/java/android/service/intelligence/FillRequest.java
new file mode 100644
index 0000000..95e9224
--- /dev/null
+++ b/core/java/android/service/intelligence/FillRequest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.Nullable;
+import android.annotation.SystemApi;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.view.autofill.AutofillId;
+
+/**
+ * Represents a request to augment-fill an activity.
+ * @hide
+ */
+@SystemApi
+public final class FillRequest {
+
+ final AutofillProxy mProxy;
+
+ /** @hide */
+ FillRequest(@NonNull AutofillProxy proxy) {
+ mProxy = proxy;
+ }
+
+ /**
+ * Gets the session associated with this request.
+ */
+ @NonNull
+ public InteractionSessionId getSessionId() {
+ return mProxy.sessionId;
+ }
+
+ /**
+ * Gets the id of the field that triggered the request.
+ */
+ @NonNull
+ public AutofillId getFocusedId() {
+ return mProxy.focusedId;
+ }
+
+ /**
+ * Gets the Smart Suggestions object used to embed the autofill UI.
+ *
+ * @return object used to embed the autofill UI, or {@code null} if not supported.
+ */
+ @Nullable
+ public PresentationParams getPresentationParams() {
+ return mProxy.getSmartSuggestionParams();
+ }
+
+ @Override
+ public String toString() {
+ return "FillRequest[id=" + mProxy.focusedId + "]";
+ }
+}
diff --git a/core/java/android/service/intelligence/FillResponse.java b/core/java/android/service/intelligence/FillResponse.java
new file mode 100644
index 0000000..860c027
--- /dev/null
+++ b/core/java/android/service/intelligence/FillResponse.java
@@ -0,0 +1,129 @@
+/*
+ * 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.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+import java.util.List;
+
+/**
+ * Response to a {@link FillRequest}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillResponse implements Parcelable {
+
+ private final FillWindow mFillWindow;
+
+ private FillResponse(@NonNull Builder builder) {
+ mFillWindow = builder.mFillWindow;
+ }
+
+ /** @hide */
+ @Nullable
+ FillWindow getFillWindow() {
+ return mFillWindow;
+ }
+
+ /**
+ * Builder for {@link FillResponse} objects.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static class Builder {
+
+ private FillWindow mFillWindow;
+
+ /**
+ * Sets the {@link FillWindow} used to display the Autofill UI.
+ *
+ * <p>Must be called when the service is handling the request so the Android System can
+ * properly synchronize the UI.
+ *
+ * @return this builder
+ */
+ public Builder setFillWindow(@NonNull FillWindow fillWindow) {
+ // TODO(b/111330312): implement / check not null / unit test
+ // TODO(b/111330312): throw exception if FillWindow not updated yet
+ mFillWindow = fillWindow;
+ return this;
+ }
+
+ /**
+ * Tells the Android System that the given {@code ids} should not trigger further
+ * {@link FillRequest requests} when focused.
+ *
+ * @param ids ids of the fields that should be ignored
+ *
+ * @return this builder
+ */
+ public Builder setIgnoredIds(@NonNull List<AutofillId> ids) {
+ // TODO(b/111330312): implement / check not null / unit test
+ return this;
+ }
+
+ /**
+ * Builds a new {@link FillResponse} instance.
+ *
+ * @throws IllegalStateException if any of the following conditions occur:
+ * <ol>
+ * <li>{@link #build()} was already called.
+ * <li>No call was made to {@link #setFillWindow(FillWindow)} or
+ * {@ling #setIgnoredIds(List<AutofillId>)}.
+ * </ol>
+ *
+ * @return A built response.
+ */
+ public FillResponse build() {
+ // TODO(b/111330312): check conditions / add unit test
+ return new FillResponse(this);
+ }
+
+ // TODO(b/111330312): add methods to disable app / activity, either here or on manager
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ // TODO(b/111330312): implement
+ }
+
+ public static final Parcelable.Creator<FillResponse> CREATOR =
+ new Parcelable.Creator<FillResponse>() {
+
+ @Override
+ public FillResponse createFromParcel(Parcel parcel) {
+ // TODO(b/111330312): implement
+ return null;
+ }
+
+ @Override
+ public FillResponse[] newArray(int size) {
+ return new FillResponse[size];
+ }
+ };
+}
diff --git a/core/java/android/service/intelligence/FillWindow.java b/core/java/android/service/intelligence/FillWindow.java
new file mode 100644
index 0000000..4ea07bf
--- /dev/null
+++ b/core/java/android/service/intelligence/FillWindow.java
@@ -0,0 +1,215 @@
+/*
+ * 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 static android.service.intelligence.IntelligenceService.DEBUG;
+
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Dialog;
+import android.graphics.Rect;
+import android.service.intelligence.PresentationParams.Area;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Handle to a window used to display the augmented autofill UI.
+ *
+ * <p>The steps to create an augmented autofill UI are:
+ *
+ * <ol>
+ * <li>Gets the {@link PresentationParams} from the {@link FillRequest}.
+ * <li>Gets the {@link Area} to display the UI (for example, through
+ * {@link PresentationParams#getSuggestionArea()}.
+ * <li>Creates a {@link View} that must fit in the {@link Area#getBounds() area boundaries}.
+ * <li>Set the proper listeners to the view (for example, a click listener that
+ * triggers {@link FillController#autofill(java.util.List)}
+ * <li>Call {@link #update(Area, View, long)} with these arguments.
+ * <li>Create a {@link FillResponse} with the {@link FillWindow}.
+ * <li>Pass such {@link FillResponse} to {@link FillCallback#onSuccess(FillResponse)}.
+ * </ol>
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillWindow {
+ private static final String TAG = "FillWindow";
+
+ /** Indicates the data being shown is a physical address */
+ public static final long FLAG_METADATA_ADDRESS = 0x1;
+
+ // TODO(b/111330312): add moar flags
+
+ /** @hide */
+ @LongDef(prefix = { "FLAG" }, value = {
+ FLAG_METADATA_ADDRESS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Flags{}
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private Dialog mDialog;
+
+ @GuardedBy("mLock")
+ private boolean mDestroyed;
+
+ /**
+ * Updates the content of the window.
+ *
+ * @param rootView new root view
+ * @param area coordinates to render the view.
+ * @param flags optional flags such as metadata of what will be rendered in the window. The
+ * Smart Suggestion host might decide whether or not to render the UI based on them.
+ *
+ * @return boolean whether the window was updated or not.
+ *
+ * @throws IllegalArgumentException if the area is not compatible with this window
+ */
+ public boolean update(@NonNull Area area, @NonNull View rootView, @Flags long flags) {
+ if (DEBUG) {
+ Log.d(TAG, "Updating " + area + " + with " + rootView);
+ }
+ // TODO(b/111330312): add test case for null
+ Preconditions.checkNotNull(area);
+ Preconditions.checkNotNull(rootView);
+ // TODO(b/111330312): must check the area is a valid object returned by
+ // SmartSuggestionParams, throw IAE if not
+
+ // TODO(b/111330312): must some how pass metadata to the SmartSuggestiongs provider
+
+
+ // TODO(b/111330312): use a SurfaceControl approach; for now, we're manually creating
+ // the window underneath the existing view.
+
+ final PresentationParams smartSuggestion = area.proxy.getSmartSuggestionParams();
+ if (smartSuggestion == null) {
+ Log.w(TAG, "No SmartSuggestionParams");
+ return false;
+ }
+
+ final Rect rect = area.getBounds();
+ if (rect == null) {
+ Log.wtf(TAG, "No Rect on SmartSuggestionParams");
+ return false;
+ }
+
+ synchronized (mLock) {
+ checkNotDestroyedLocked();
+
+ // TODO(b/111330312): once we have the SurfaceControl approach, we should update the
+ // window instead of destroying. In fact, it might be better to allocate a full window
+ // initially, which is transparent (and let touches get through) everywhere but in the
+ // rect boundaries.
+ destroy();
+
+ // TODO(b/111330312): make sure all touch events are handled, window is always closed,
+ // etc.
+
+ mDialog = new Dialog(rootView.getContext());
+ final Window window = mDialog.getWindow();
+ window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+
+ final int height = rect.bottom - rect.top;
+ final int width = rect.right - rect.left;
+ final WindowManager.LayoutParams windowParams = window.getAttributes();
+ windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ windowParams.y = rect.top - height;
+ windowParams.height = height;
+ windowParams.x = rect.left;
+ windowParams.width = width;
+
+ window.setAttributes(windowParams);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+
+ mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
+ mDialog.setContentView(rootView, diagParams);
+
+ if (DEBUG) {
+ Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
+ }
+
+ area.proxy.setFillWindow(this);
+ return true;
+ }
+ }
+
+ /** @hide */
+ void show() {
+ // TODO(b/111330312): check if updated first / throw exception
+ if (DEBUG) Log.d(TAG, "show()");
+
+ synchronized (mLock) {
+ checkNotDestroyedLocked();
+ if (mDialog == null) {
+ throw new IllegalStateException("update() not called yet, or already destroyed()");
+ }
+
+ mDialog.show();
+ }
+ }
+
+ /**
+ * Destroys the window.
+ *
+ * <p>Once destroyed, this window cannot be used anymore
+ */
+ public void destroy() {
+ if (DEBUG) Log.d(TAG, "destroy(): mDestroyed = " + mDestroyed);
+
+ synchronized (this) {
+ if (mDestroyed) return;
+
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ }
+ }
+
+ private void checkNotDestroyedLocked() {
+ if (mDestroyed) {
+ throw new IllegalStateException("already destroyed()");
+ }
+ }
+
+ /** @hide */
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ synchronized (this) {
+ pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+ if (mDialog != null) {
+ pw.print(prefix); pw.print("dialog: ");
+ pw.println(mDialog.isShowing() ? "shown" : "hidden");
+ pw.print(prefix); pw.print("window: ");
+ pw.println(mDialog.getWindow().getAttributes());
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
index 709c3b7..e2260d7 100644
--- a/core/java/android/service/intelligence/IIntelligenceService.aidl
+++ b/core/java/android/service/intelligence/IIntelligenceService.aidl
@@ -16,10 +16,12 @@
package android.service.intelligence;
+import android.os.IBinder;
import android.service.intelligence.InteractionSessionId;
import android.service.intelligence.InteractionContext;
import android.service.intelligence.SnapshotData;
+import android.view.autofill.AutofillId;
import android.view.intelligence.ContentCaptureEvent;
import java.util.List;
@@ -40,4 +42,9 @@
void onActivitySnapshot(in InteractionSessionId sessionId,
in SnapshotData snapshotData);
+
+ void onAutofillRequest(in InteractionSessionId sessionId, in IBinder autofillManagerClient,
+ int autofilSessionId, in AutofillId focusedId);
+
+ void onDestroyAutofillWindowsRequest(in InteractionSessionId sessionId);
}
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java
index 27569b6..040e25e 100644
--- a/core/java/android/service/intelligence/IntelligenceService.java
+++ b/core/java/android/service/intelligence/IntelligenceService.java
@@ -22,13 +22,26 @@
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.graphics.Rect;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.service.intelligence.PresentationParams.SystemPopupPresentationParams;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.view.autofill.IAugmentedAutofillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -44,6 +57,9 @@
private static final String TAG = "IntelligenceService";
+ // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
+ static final boolean DEBUG = true;
+
/**
* The {@link Intent} that must be declared as handled by the service.
* To be supported, the service must also require the
@@ -55,6 +71,8 @@
private Handler mHandler;
+ private ArrayMap<InteractionSessionId, AutofillProxy> mAutofillProxies;
+
private final IIntelligenceService mInterface = new IIntelligenceService.Stub() {
@Override
@@ -87,6 +105,20 @@
obtainMessage(IntelligenceService::onActivitySnapshot,
IntelligenceService.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));
+ }
+
+ @Override
+ public void onDestroyAutofillWindowsRequest(InteractionSessionId sessionId) {
+ mHandler.sendMessage(
+ obtainMessage(IntelligenceService::handleOnDestroyAutofillWindowsRequest,
+ IntelligenceService.this, sessionId));
+ }
};
@CallSuper
@@ -122,10 +154,93 @@
* @param sessionId the session's Id
* @param events the events
*/
- // TODO(b/111276913): rename to onContentCaptureEvents
+ // 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);
+ private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId,
+ @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId) {
+ if (mAutofillProxies == null) {
+ mAutofillProxies = new ArrayMap<>();
+ }
+ AutofillProxy proxy = mAutofillProxies.get(sessionId);
+ if (proxy == null) {
+ proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId);
+ mAutofillProxies.put(sessionId, proxy);
+ } else {
+ // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
+ if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
+ }
+ // TODO(b/111330312): set cancellation signal
+ final CancellationSignal cancellationSignal = null;
+ onFillRequest(sessionId, new FillRequest(proxy), cancellationSignal,
+ new FillController(proxy), new FillCallback());
+ }
+
+ /**
+ * Asks the service to handle an "augmented" autofill request.
+ *
+ * <p>This method is called when the "stantard" autofill service cannot handle a request, which
+ * typically occurs when:
+ * <ul>
+ * <li>Service does not recognize what should be autofilled.
+ * <li>Service does not have data to fill the request.
+ * <li>Service blacklisted that app (or activity) for autofill.
+ * <li>App disabled itself for autofill.
+ * </ul>
+ *
+ * <p>Differently from the standard autofill workflow, on augmented autofill the service is
+ * responsible to generate the autofill UI and request the Android system to autofill the
+ * activity when the user taps an action in that UI (through the
+ * {@link FillController#autofill(List)} method).
+ *
+ * <p>The service <b>MUST</b> call {@link
+ * FillCallback#onSuccess(android.service.intelligence.FillResponse)} as soon as possible,
+ * passing {@code null} when it cannot fulfill the request.
+ *
+ * @param sessionId the session's id
+ * @param request the request to handle.
+ * @param cancellationSignal signal for observing cancellation requests. The system will use
+ * this to notify you that the fill result is no longer needed and you should stop
+ * handling this fill request in order to save resources.
+ * @param controller object used to interact with the autofill system.
+ * @param callback object used to notify the result of the request. Service <b>must</b> call
+ * {@link FillCallback#onSuccess(android.service.intelligence.FillResponse)}.
+ */
+ public void onFillRequest(@NonNull InteractionSessionId sessionId, @NonNull FillRequest request,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillController controller,
+ @NonNull FillCallback callback) {
+ }
+
+ private void handleOnDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
+ AutofillProxy proxy = null;
+ if (mAutofillProxies != null) {
+ proxy = mAutofillProxies.get(sessionId);
+ }
+ if (proxy == null) {
+ // TODO(b/111330312): this might be fine, in which case we should logv it
+ Log.w(TAG, "No proxy for session " + sessionId);
+ return;
+ }
+ proxy.destroy();
+ mAutofillProxies.remove(sessionId);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mAutofillProxies != null) {
+ final int size = mAutofillProxies.size();
+ pw.print("Number proxies: "); pw.println(size);
+ for (int i = 0; i < size; i++) {
+ final InteractionSessionId sessionId = mAutofillProxies.keyAt(i);
+ final AutofillProxy proxy = mAutofillProxies.valueAt(i);
+ pw.print(i); pw.print(") SessionId="); pw.print(sessionId); pw.println(":");
+ proxy.dump(" ", pw);
+ }
+ }
+ }
+
/**
* Notifies the service of {@link IntelligenceSnapshotData snapshot data} associated with a
* session.
@@ -142,4 +257,99 @@
* @param sessionId the id of the session to destroy
*/
public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {}
+
+ /** @hide */
+ static final class AutofillProxy {
+ private final Object mLock = new Object();
+ private final IAugmentedAutofillManagerClient mClient;
+ private final int mAutofillSessionId;
+ public final InteractionSessionId sessionId;
+ public final AutofillId focusedId;
+
+ @GuardedBy("mLock")
+ private SystemPopupPresentationParams mSmartSuggestion;
+
+ @GuardedBy("mLock")
+ private FillWindow mFillWindow;
+
+ private AutofillProxy(@NonNull InteractionSessionId sessionId, @NonNull IBinder client,
+ int autofillSessionId, @NonNull AutofillId focusedId) {
+ this.sessionId = sessionId;
+ mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
+ mAutofillSessionId = autofillSessionId;
+ this.focusedId = focusedId;
+ // TODO(b/111330312): linkToDeath
+ }
+
+ @NonNull
+ public SystemPopupPresentationParams getSmartSuggestionParams() {
+ synchronized (mLock) {
+ if (mSmartSuggestion != null) {
+ return mSmartSuggestion;
+ }
+ Rect rect;
+ try {
+ rect = mClient.getViewCoordinates(focusedId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not get coordinates for " + focusedId);
+ return null;
+ }
+ if (rect == null) {
+ if (DEBUG) Log.d(TAG, "getViewCoordinates(" + focusedId + ") returned null");
+ return null;
+ }
+ mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
+ return mSmartSuggestion;
+ }
+ }
+
+ public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> pairs)
+ throws RemoteException {
+ final int size = pairs.size();
+ final List<AutofillId> ids = new ArrayList<>(size);
+ final List<AutofillValue> values = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ final Pair<AutofillId, AutofillValue> pair = pairs.get(i);
+ ids.add(pair.first);
+ values.add(pair.second);
+ }
+ mClient.autofill(mAutofillSessionId, ids, values);
+ }
+
+ public void setFillWindow(@NonNull FillWindow fillWindow) {
+ synchronized (mLock) {
+ mFillWindow = fillWindow;
+ }
+ }
+
+ public FillWindow getFillWindow() {
+ synchronized (mLock) {
+ return mFillWindow;
+ }
+ }
+
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix); pw.print("afSessionId: "); pw.println(mAutofillSessionId);
+ pw.print(prefix); pw.print("focusedId: "); pw.println(focusedId);
+ pw.print(prefix); pw.print("client: "); pw.println(mClient);
+ final String prefix2 = prefix + " ";
+ if (mFillWindow != null) {
+ pw.print(prefix); pw.println("window:");
+ mFillWindow.dump(prefix2, pw);
+ }
+ if (mSmartSuggestion != null) {
+ pw.print(prefix); pw.println("smartSuggestion:");
+ mSmartSuggestion.dump(prefix2, pw);
+ }
+ }
+
+ private void destroy() {
+ synchronized (mLock) {
+ if (mFillWindow != null) {
+ if (DEBUG) Log.d(TAG, "destroying window");
+ mFillWindow.destroy();
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/service/intelligence/PresentationParams.java b/core/java/android/service/intelligence/PresentationParams.java
new file mode 100644
index 0000000..c59069b
--- /dev/null
+++ b/core/java/android/service/intelligence/PresentationParams.java
@@ -0,0 +1,225 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.util.DebugUtils;
+import android.view.View;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by
+ * the intelligence service.
+ *
+ * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places:
+ *
+ * <ul>
+ * <li>A small area associated with suggestions (like a small strip in the top of the IME),
+ * returned by {@link #getSuggestionArea()}
+ * <li>The full area (like the full IME window), returned by {@link #getFullArea()}
+ * <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)}
+ * </ul>
+ *
+ * <p>The Smart Suggestion is represented by a {@link Area} object that contains the
+ * dimensions the smart suggestion window, so the service can use it to calculate the size of the
+ * view that will be passed to {@link FillWindow#update(Area, View, long)}.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class PresentationParams {
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the top of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_TOP = 0x1;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the bottom of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the left of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_LEFT = 0x4;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the right of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted by the IME.
+ */
+ public static final int FLAG_HOST_IME = 0x10;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup
+ * window.
+ */
+ public static final int FLAG_HOST_SYSTEM = 0x20;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_HINT_GRAVITY_TOP,
+ FLAG_HINT_GRAVITY_BOTTOM,
+ FLAG_HINT_GRAVITY_LEFT,
+ FLAG_HINT_GRAVITY_RIGHT,
+ FLAG_HOST_IME,
+ FLAG_HOST_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Flags {}
+
+
+ // /** @hide */
+ PresentationParams() {}
+
+ /**
+ * Gets the area of the suggestion strip for the given {@code metadata}
+ *
+ * @return strip dimensions, or {@code null} if the Smart Suggestion provider does not support
+ * suggestions strip.
+ */
+ @Nullable
+ public Area getSuggestionArea() {
+ return null;
+ }
+
+ /**
+ * Gets the full area for the of the Smart Suggestion provider.
+ *
+ * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support
+ * embeding the UI on its full area.
+ */
+ @Nullable
+ public Area getFullArea() {
+ return null;
+ }
+
+ /**
+ * Gets flags associated with the Smart Suggestion.
+ *
+ * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP},
+ * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT},
+ * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or
+ * {@link #FLAG_HOST_SYSTEM},
+ */
+ public @Flags int getFlags() {
+ return 0;
+ }
+
+ /** @hide */
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ final int flags = getFlags();
+ if (flags > 0) {
+ pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags));
+ }
+ }
+
+ private static String flagsToString(int flags) {
+ return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags);
+ }
+
+ /**
+ * Area associated with a {@link PresentationParams Smart Suggestions} provider.
+ *
+ * @hide
+ * */
+ @SystemApi
+ public abstract static class Area {
+
+ /** @hide */
+ public final AutofillProxy proxy;
+
+ private final Rect mBounds;
+
+ private Area(@NonNull AutofillProxy proxy, @NonNull Rect bounds) {
+ this.proxy = proxy;
+ mBounds = bounds;
+ }
+
+ /**
+ * Gets the area boundaries.
+ */
+ @NonNull
+ public Rect getBounds() {
+ return mBounds;
+ }
+
+ /**
+ * Gets a subarea limited by given boundaries.
+ *
+ * @param bounds boundaries relative to this Area.
+ *
+ * @throws {@link IllegalArgumentException} if the {@code bounds} is not fully-contained
+ * inside this full Area.
+ *
+ * @return new subarea, or {@code null} if the Smart Suggestion host does not support such
+ * subaarea.
+ */
+ @Nullable
+ public Area getSubArea(@NonNull Rect bounds) {
+ // TODO(b/111330312): implement / check boundaries / throw IAE / add unit test
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return mBounds.toString();
+ }
+ }
+
+ /**
+ * System-provided poup window anchored to a view.
+ *
+ * <p>Used just for debugging purposes.
+ *
+ * @hide
+ */
+ public static final class SystemPopupPresentationParams extends PresentationParams {
+ private final Area mSuggestionArea;
+
+ public SystemPopupPresentationParams(@NonNull AutofillProxy proxy, @NonNull Rect rect) {
+ mSuggestionArea = new Area(proxy, rect) {};
+ }
+
+ @Override
+ public Area getSuggestionArea() {
+ return mSuggestionArea;
+ }
+
+ @Override
+ public int getFlags() {
+ return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM;
+ }
+
+ @Override
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ super.dump(prefix, pw);
+ pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea);
+ }
+ }
+}
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index d8bd002..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;
@@ -47,4 +48,8 @@
void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel);
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
void onNotificationsSeen(in List<String> keys);
+ 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 c1a3c2b..c850a4e 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -16,9 +16,14 @@
package android.service.notification;
+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;
@@ -33,6 +38,7 @@
import com.android.internal.os.SomeArgs;
+import java.lang.annotation.Retention;
import java.util.List;
/**
@@ -63,6 +69,13 @@
public abstract class NotificationAssistantService extends NotificationListenerService {
private static final String TAG = "NotificationAssistants";
+ /** @hide */
+ @Retention(SOURCE)
+ @IntDef({SOURCE_FROM_APP, SOURCE_FROM_ASSISTANT})
+ public @interface Source {}
+ public static final int SOURCE_FROM_APP = 0;
+ public static final int SOURCE_FROM_ASSISTANT = 1;
+
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -160,6 +173,38 @@
}
/**
+ * Implement this to know when a notification is expanded / collapsed.
+ * @param key the notification key
+ * @param isUserAction whether the expanded change is caused by user action.
+ * @param isExpanded whether the notification is expanded.
+ */
+ public void onNotificationExpansionChanged(
+ String key, boolean isUserAction, boolean isExpanded) {}
+
+ /**
+ * Implement this to know when a direct reply is sent from a notification.
+ * @param key the notification key
+ */
+ public void onNotificationDirectReply(String key) {}
+
+ /**
+ * 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 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.
@@ -255,12 +300,53 @@
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATIONS_SEEN,
args).sendToTarget();
}
+
+ @Override
+ public void onNotificationExpansionChanged(String key, boolean isUserAction,
+ boolean isExpanded) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.argi1 = isUserAction ? 1 : 0;
+ args.argi2 = isExpanded ? 1 : 0;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_EXPANSION_CHANGED, args)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT, args)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.arg2 = reply;
+ 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 {
public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
public static final int MSG_ON_NOTIFICATION_SNOOZED = 2;
public static final int MSG_ON_NOTIFICATIONS_SEEN = 3;
+ 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);
@@ -305,6 +391,40 @@
onNotificationsSeen(keys);
break;
}
+ case MSG_ON_NOTIFICATION_EXPANSION_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ boolean isUserAction = args.argi1 == 1;
+ boolean isExpanded = args.argi2 == 1;
+ args.recycle();
+ onNotificationExpansionChanged(key, isUserAction, isExpanded);
+ break;
+ }
+ case MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ args.recycle();
+ onNotificationDirectReply(key);
+ break;
+ }
+ case MSG_ON_SUGGESTED_REPLY_SENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ CharSequence reply = (CharSequence) args.arg2;
+ int source = args.argi2;
+ args.recycle();
+ 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 64eae0c..1fe97b7 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1366,6 +1366,27 @@
}
@Override
+ public void onNotificationExpansionChanged(
+ String key, boolean isUserAction, boolean isExpanded) {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+ // no-op in the listener
+ }
+
+ @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/sms/FinancialSmsService.java b/core/java/android/service/sms/FinancialSmsService.java
new file mode 100644
index 0000000..5fb7249
--- /dev/null
+++ b/core/java/android/service/sms/FinancialSmsService.java
@@ -0,0 +1,104 @@
+/*
+ * 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.sms;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.database.CursorWindow;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+/**
+ * A service to support sms messages read for financial apps.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class FinancialSmsService extends Service {
+
+ private static final String TAG = "FinancialSmsService";
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service
+ * in its manifest for the system to recognize it as a quota providing
+ * service.
+ */
+ public static final String ACTION_FINANCIAL_SERVICE_INTENT =
+ "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+
+ /** {@hide} **/
+ public static final String EXTRA_SMS_MSGS = "sms_messages";
+
+ private FinancialSmsServiceWrapper mWrapper;
+
+ private void getSmsMessages(RemoteCallback callback, Bundle params) {
+ final Bundle data = new Bundle();
+ CursorWindow smsMessages = onGetSmsMessages(params);
+ if (smsMessages != null) {
+ data.putParcelable(EXTRA_SMS_MSGS, smsMessages);
+ }
+ callback.sendResult(data);
+ }
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+ /** @hide */
+ public FinancialSmsService() {
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWrapper = new FinancialSmsServiceWrapper();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mWrapper;
+ }
+
+ /**
+ * Get sms messages for financial apps.
+ *
+ * @param params parameters passed in by the calling app.
+ * @return the {@code CursorWindow} with all sms messages for the app to read.
+ *
+ * {@hide}
+ */
+ @Nullable
+ @SystemApi
+ public abstract CursorWindow onGetSmsMessages(@NonNull Bundle params);
+
+ private final class FinancialSmsServiceWrapper extends IFinancialSmsService.Stub {
+ @Override
+ public void getSmsMessages(RemoteCallback callback, Bundle params) throws RemoteException {
+ mHandler.sendMessage(obtainMessage(
+ FinancialSmsService::getSmsMessages,
+ FinancialSmsService.this,
+ callback, params));
+ }
+ }
+
+}
diff --git a/media/java/android/media/ISessionTokensListener.aidl b/core/java/android/service/sms/IFinancialSmsService.aidl
similarity index 67%
rename from media/java/android/media/ISessionTokensListener.aidl
rename to core/java/android/service/sms/IFinancialSmsService.aidl
index c83a19e..caabe58 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/android/service/sms/IFinancialSmsService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,14 +14,17 @@
* limitations under the License.
*/
-package android.media;
+package android.service.sms;
import android.os.Bundle;
+import android.os.RemoteCallback;
/**
- * Listens for changes to the list of session tokens.
+ * Service used by financial apps to read sms messages.
+ *
* @hide
*/
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
-}
+oneway interface IFinancialSmsService
+{
+ void getSmsMessages(in RemoteCallback callback, in Bundle params);
+}
\ No newline at end of file
diff --git a/media/java/android/media/ISessionTokensListener.aidl b/core/java/android/service/textclassifier/IConversationActionsCallback.aidl
similarity index 63%
copy from media/java/android/media/ISessionTokensListener.aidl
copy to core/java/android/service/textclassifier/IConversationActionsCallback.aidl
index c83a19e..c35d424 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/android/service/textclassifier/IConversationActionsCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,14 +14,15 @@
* limitations under the License.
*/
-package android.media;
+package android.service.textclassifier;
-import android.os.Bundle;
+import android.view.textclassifier.ConversationActions;
/**
- * Listens for changes to the list of session tokens.
+ * Callback for a ConversationActions request.
* @hide
*/
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
-}
+oneway interface IConversationActionsCallback {
+ void onSuccess(in ConversationActions conversationActions);
+ void onFailure();
+}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/ITextClassifierService.aidl b/core/java/android/service/textclassifier/ITextClassifierService.aidl
index 7ac72c7..254a710 100644
--- a/core/java/android/service/textclassifier/ITextClassifierService.aidl
+++ b/core/java/android/service/textclassifier/ITextClassifierService.aidl
@@ -16,14 +16,18 @@
package android.service.textclassifier;
+import android.service.textclassifier.IConversationActionsCallback;
import android.service.textclassifier.ITextClassificationCallback;
+import android.service.textclassifier.ITextLanguageCallback;
import android.service.textclassifier.ITextLinksCallback;
import android.service.textclassifier.ITextSelectionCallback;
+import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationSessionId;
import android.view.textclassifier.TextLinks;
+import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextSelection;
/**
@@ -58,4 +62,14 @@
void onDestroyTextClassificationSession(
in TextClassificationSessionId sessionId);
+
+ void onDetectLanguage(
+ in TextClassificationSessionId sessionId,
+ in TextLanguage.Request request,
+ in ITextLanguageCallback callback);
+
+ void onSuggestConversationActions(
+ in TextClassificationSessionId sessionId,
+ in ConversationActions.Request request,
+ in IConversationActionsCallback callback);
}
diff --git a/media/java/android/media/ISessionTokensListener.aidl b/core/java/android/service/textclassifier/ITextLanguageCallback.aidl
similarity index 65%
copy from media/java/android/media/ISessionTokensListener.aidl
copy to core/java/android/service/textclassifier/ITextLanguageCallback.aidl
index c83a19e..263d99af 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/android/service/textclassifier/ITextLanguageCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,14 +14,15 @@
* limitations under the License.
*/
-package android.media;
+package android.service.textclassifier;
-import android.os.Bundle;
+import android.view.textclassifier.TextLanguage;
/**
- * Listens for changes to the list of session tokens.
+ * Callback for a TextLanguage request.
* @hide
*/
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
-}
+oneway interface ITextLanguageCallback {
+ void onSuccess(in TextLanguage textLanguage);
+ void onFailure();
+}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 7f1082d..d7359f1 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -32,12 +32,14 @@
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Slog;
+import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
@@ -92,8 +94,7 @@
@Override
public void onSuggestSelection(
TextClassificationSessionId sessionId,
- TextSelection.Request request, ITextSelectionCallback callback)
- throws RemoteException {
+ TextSelection.Request request, ITextSelectionCallback callback) {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
TextClassifierService.this.onSuggestSelection(
@@ -125,8 +126,7 @@
@Override
public void onClassifyText(
TextClassificationSessionId sessionId,
- TextClassification.Request request, ITextClassificationCallback callback)
- throws RemoteException {
+ TextClassification.Request request, ITextClassificationCallback callback) {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
TextClassifierService.this.onClassifyText(
@@ -156,8 +156,7 @@
@Override
public void onGenerateLinks(
TextClassificationSessionId sessionId,
- TextLinks.Request request, ITextLinksCallback callback)
- throws RemoteException {
+ TextLinks.Request request, ITextLinksCallback callback) {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
TextClassifierService.this.onGenerateLinks(
@@ -188,16 +187,81 @@
@Override
public void onSelectionEvent(
TextClassificationSessionId sessionId,
- SelectionEvent event) throws RemoteException {
+ SelectionEvent event) {
Preconditions.checkNotNull(event);
TextClassifierService.this.onSelectionEvent(sessionId, event);
}
/** {@inheritDoc} */
@Override
+ public void onDetectLanguage(
+ TextClassificationSessionId sessionId,
+ TextLanguage.Request request,
+ ITextLanguageCallback callback) {
+ Preconditions.checkNotNull(request);
+ Preconditions.checkNotNull(callback);
+ TextClassifierService.this.onDetectLanguage(
+ sessionId,
+ request,
+ mCancellationSignal,
+ new Callback<TextLanguage>() {
+ @Override
+ public void onSuccess(TextLanguage result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ Slog.d(LOG_TAG, "Error calling callback");
+ }
+ }
+
+ @Override
+ public void onFailure(CharSequence error) {
+ try {
+ callback.onFailure();
+ } catch (RemoteException e) {
+ Slog.d(LOG_TAG, "Error calling callback");
+ }
+ };
+ });
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onSuggestConversationActions(
+ TextClassificationSessionId sessionId,
+ ConversationActions.Request request,
+ IConversationActionsCallback callback) {
+ Preconditions.checkNotNull(request);
+ Preconditions.checkNotNull(callback);
+ TextClassifierService.this.onSuggestConversationActions(
+ sessionId,
+ request,
+ mCancellationSignal,
+ new Callback<ConversationActions>() {
+ @Override
+ public void onSuccess(ConversationActions result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ Slog.d(LOG_TAG, "Error calling callback");
+ }
+ }
+
+ @Override
+ public void onFailure(CharSequence error) {
+ try {
+ callback.onFailure();
+ } catch (RemoteException e) {
+ Slog.d(LOG_TAG, "Error calling callback");
+ }
+ }
+ });
+ }
+
+ /** {@inheritDoc} */
+ @Override
public void onCreateTextClassificationSession(
- TextClassificationContext context, TextClassificationSessionId sessionId)
- throws RemoteException {
+ TextClassificationContext context, TextClassificationSessionId sessionId) {
Preconditions.checkNotNull(context);
Preconditions.checkNotNull(sessionId);
TextClassifierService.this.onCreateTextClassificationSession(context, sessionId);
@@ -205,8 +269,7 @@
/** {@inheritDoc} */
@Override
- public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId)
- throws RemoteException {
+ public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId) {
TextClassifierService.this.onDestroyTextClassificationSession(sessionId);
}
};
@@ -266,6 +329,38 @@
@NonNull Callback<TextLinks> callback);
/**
+ * Detects and returns the language of the give text.
+ *
+ * @param sessionId the session id
+ * @param request the language detection request
+ * @param cancellationSignal object to watch for canceling the current operation
+ * @param callback the callback to return the result to
+ */
+ public void onDetectLanguage(
+ @Nullable TextClassificationSessionId sessionId,
+ @NonNull TextLanguage.Request request,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull Callback<TextLanguage> callback) {
+ callback.onSuccess(getLocalTextClassifier().detectLanguage(request));
+ }
+
+ /**
+ * Suggests and returns a list of actions according to the given conversation.
+ *
+ * @param sessionId the session id
+ * @param request the conversation actions request
+ * @param cancellationSignal object to watch for canceling the current operation
+ * @param callback the callback to return the result to
+ */
+ public void onSuggestConversationActions(
+ @Nullable TextClassificationSessionId sessionId,
+ @NonNull ConversationActions.Request request,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull Callback<ConversationActions> callback) {
+ callback.onSuccess(getLocalTextClassifier().suggestConversationActions(request));
+ }
+
+ /**
* Writes the selection event.
* This is called when a selection event occurs. e.g. user changed selection; or smart selection
* happened.
diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
index 3c3ef0c..a976d0e 100644
--- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -24,8 +24,8 @@
* @hide
*/
interface IWallpaperConnection {
- void attachEngine(IWallpaperEngine engine);
- void engineShown(IWallpaperEngine engine);
+ void attachEngine(IWallpaperEngine engine, int displayId);
+ void engineShown(IWallpaperEngine engine);
ParcelFileDescriptor setWallpaper(String name);
void onWallpaperColorsChanged(in WallpaperColors colors);
}
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/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl
index 5fd0157..99a81f5 100644
--- a/core/java/android/service/wallpaper/IWallpaperService.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperService.aidl
@@ -24,6 +24,6 @@
*/
oneway interface IWallpaperService {
void attach(IWallpaperConnection connection,
- IBinder windowToken, int windowType, boolean isPreview,
- int reqWidth, int reqHeight, in Rect padding);
+ IBinder windowToken, int windowType, boolean isPreview,
+ int reqWidth, int reqHeight, in Rect padding, int displayId);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4bd86a4..a095b0d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -19,12 +19,12 @@
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;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
-import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -57,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;
@@ -130,7 +131,7 @@
Bundle extras;
boolean sync;
}
-
+
/**
* The actual implementation of a wallpaper. A wallpaper service may
* have multiple instances running (for example as a real wallpaper
@@ -145,7 +146,7 @@
HandlerCaller mCaller;
IWallpaperConnection mConnection;
IBinder mWindowToken;
-
+
boolean mInitializing = true;
boolean mVisible;
boolean mReportedVisible;
@@ -185,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
@@ -209,7 +211,6 @@
private final Supplier<Long> mClockFunction;
private final Handler mHandler;
- DisplayManager mDisplayManager;
Display mDisplay;
private int mDisplayState;
@@ -420,7 +421,7 @@
public int getDesiredMinimumHeight() {
return mIWallpaperEngine.mReqHeight;
}
-
+
/**
* Return whether the wallpaper is currently visible to the user,
* this is the last value supplied to
@@ -442,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;
}
@@ -568,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) {
}
/**
@@ -805,9 +810,11 @@
mLayout.windowAnimations =
com.android.internal.R.style.Animation_Wallpaper;
mInputChannel = new InputChannel();
+
if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
- Display.DEFAULT_DISPLAY, mWinFrame, mContentInsets, mStableInsets,
- mOutsets, mDisplayCutout, mInputChannel) < 0) {
+ mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
+ mOutsets, mDisplayCutout, mInputChannel,
+ mInsetsState) < 0) {
Log.w(TAG, "Failed to add window while updating wallpaper surface.");
return;
}
@@ -833,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);
@@ -1015,7 +1023,7 @@
if (mDestroyed) {
return;
}
-
+
mIWallpaperEngine = wrapper;
mCaller = wrapper.mCaller;
mConnection = wrapper.mConnection;
@@ -1027,16 +1035,16 @@
mWindow.setSession(mSession);
mLayout.packageName = getPackageName();
-
- mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
- mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
- mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
+ mCaller.getHandler());
+ mDisplay = mIWallpaperEngine.mDisplay;
mDisplayState = mDisplay.getState();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
onCreate(mSurfaceHolder);
-
+
mInitializing = false;
+
mReportedVisible = false;
updateSurface(false, false, false);
}
@@ -1046,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);
}
}
}
@@ -1198,8 +1206,8 @@
mDestroyed = true;
- if (mDisplayManager != null) {
- mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ if (mIWallpaperEngine.mDisplayManager != null) {
+ mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener);
}
if (mVisible) {
@@ -1268,12 +1276,16 @@
int mReqWidth;
int mReqHeight;
final Rect mDisplayPadding = new Rect();
+ final int mDisplayId;
+ final DisplayManager mDisplayManager;
+ final Display mDisplay;
Engine mEngine;
IWallpaperEngineWrapper(WallpaperService context,
IWallpaperConnection conn, IBinder windowToken,
- int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
+ int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
+ int displayId) {
mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
mConnection = conn;
mWindowToken = windowToken;
@@ -1282,7 +1294,16 @@
mReqWidth = reqWidth;
mReqHeight = reqHeight;
mDisplayPadding.set(padding);
-
+ mDisplayId = displayId;
+
+ // Create a display context before onCreateEngine.
+ mDisplayManager = getSystemService(DisplayManager.class);
+ mDisplay = mDisplayManager.getDisplay(mDisplayId);
+
+ if (mDisplay == null) {
+ // Ignore this engine.
+ throw new IllegalArgumentException("Cannot find display with id" + mDisplayId);
+ }
Message msg = mCaller.obtainMessage(DO_ATTACH);
mCaller.sendMessage(msg);
}
@@ -1304,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);
}
@@ -1353,7 +1374,7 @@
switch (message.what) {
case DO_ATTACH: {
try {
- mConnection.attachEngine(this);
+ mConnection.attachEngine(this, mDisplayId);
} catch (RemoteException e) {
Log.w(TAG, "Wallpaper host disappeared", e);
return;
@@ -1378,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:
@@ -1453,9 +1474,10 @@
@Override
public void attach(IWallpaperConnection conn, IBinder windowToken,
- int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
+ int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
+ int displayId) {
new IWallpaperEngineWrapper(mTarget, conn, windowToken,
- windowType, isPreview, reqWidth, reqHeight, padding);
+ windowType, isPreview, reqWidth, reqHeight, padding, displayId);
}
}
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index ec63cd9..7815864 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1009,7 +1009,7 @@
Log.e(TAG, "null synthesis text");
return false;
}
- if (mText.length() >= TextToSpeech.getMaxSpeechInputLength()) {
+ if (mText.length() > TextToSpeech.getMaxSpeechInputLength()) {
Log.w(TAG, "Text too long: " + mText.length() + " chars");
return false;
}
@@ -1609,7 +1609,7 @@
synchronized (mCallerToCallback) {
mCallerToCallback.remove(caller);
}
- //mSynthHandler.stopForApp(caller);
+ mSynthHandler.stopForApp(caller);
}
@Override
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/TextUtils.java b/core/java/android/text/TextUtils.java
index b970c25..85b6b88 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -46,6 +46,7 @@
import android.text.style.ForegroundColorSpan;
import android.text.style.LeadingMarginSpan;
import android.text.style.LineBackgroundSpan;
+import android.text.style.LineHeightSpan;
import android.text.style.LocaleSpan;
import android.text.style.ParagraphStyle;
import android.text.style.QuoteSpan;
@@ -733,7 +734,9 @@
/** @hide */
public static final int LINE_BACKGROUND_SPAN = 27;
/** @hide */
- public static final int LAST_SPAN = LINE_BACKGROUND_SPAN;
+ public static final int LINE_HEIGHT_SPAN = 28;
+ /** @hide */
+ public static final int LAST_SPAN = LINE_HEIGHT_SPAN;
/**
* Flatten a CharSequence and whatever styles can be copied across processes
@@ -928,6 +931,10 @@
readSpan(p, sp, new LineBackgroundSpan.Standard(p));
break;
+ case LINE_HEIGHT_SPAN:
+ readSpan(p, sp, new LineHeightSpan.Standard(p));
+ break;
+
default:
throw new RuntimeException("bogus span encoding " + kind);
}
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/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 2742ae0..a5d5af2 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -20,7 +20,10 @@
import android.annotation.NonNull;
import android.annotation.Px;
import android.graphics.Paint;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
import android.text.TextPaint;
+import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -71,7 +74,7 @@
* covers only part of the paragraph.
* </p>
*/
- class Standard implements LineHeightSpan {
+ class Standard implements LineHeightSpan, ParcelableSpan {
private final @Px int mHeight;
/**
@@ -82,6 +85,48 @@
mHeight = height;
}
+ /**
+ * Constructor called from {@link TextUtils} to restore the span from a parcel
+ */
+ public Standard(Parcel src) {
+ mHeight = src.readInt();
+ }
+
+ /**
+ * Returns the line height specified by this span.
+ */
+ @Px
+ public int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ @Override
+ public int getSpanTypeIdInternal() {
+ return TextUtils.LINE_HEIGHT_SPAN;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mHeight);
+ }
+
@Override
public void chooseHeight(@NonNull CharSequence text, int start, int end,
int spanstartv, int lineHeight,
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index f4dad62..eef7ea2 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -29,6 +29,7 @@
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.text.style.URLSpan;
+import android.util.Log;
import android.util.Patterns;
import android.webkit.WebView;
import android.widget.TextView;
@@ -72,6 +73,9 @@
*/
public class Linkify {
+
+ private static final String LOG_TAG = "Linkify";
+
/**
* Bit field indicating that web URLs should be matched in methods that
* take an options mask
@@ -310,6 +314,11 @@
*/
private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
@Nullable Context context, @Nullable UrlSpanFactory urlSpanFactory) {
+ if (text != null && containsUnsupportedCharacters(text.toString())) {
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ return false;
+ }
+
if (mask == 0) {
return false;
}
@@ -356,6 +365,29 @@
}
/**
+ * Returns true if the specified text contains at least one unsupported character for applying
+ * links. Also logs the error.
+ *
+ * @param text the text to apply links to
+ * @hide
+ */
+ public static boolean containsUnsupportedCharacters(String text) {
+ if (text.contains("\u202C")) {
+ Log.e(LOG_TAG, "Unsupported character for applying links: u202C");
+ return true;
+ }
+ if (text.contains("\u202D")) {
+ Log.e(LOG_TAG, "Unsupported character for applying links: u202D");
+ return true;
+ }
+ if (text.contains("\u202E")) {
+ Log.e(LOG_TAG, "Unsupported character for applying links: u202E");
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Scans the text of the provided TextView and turns all occurrences of
* the link types indicated in the mask into clickable links. If matches
* are found the movement method for the TextView is set to
@@ -560,6 +592,11 @@
@Nullable String defaultScheme, @Nullable String[] schemes,
@Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter,
@Nullable UrlSpanFactory urlSpanFactory) {
+ if (spannable != null && containsUnsupportedCharacters(spannable.toString())) {
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ return false;
+ }
+
final String[] schemesCopy;
if (defaultScheme == null) defaultScheme = "";
if (schemes == null || schemes.length < 1) {
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/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 717a858..57d55bf 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -21,10 +21,10 @@
import android.annotation.UnsupportedAppUsage;
import android.os.SystemClock;
-import libcore.util.CountryTimeZones;
-import libcore.util.CountryTimeZones.TimeZoneMapping;
-import libcore.util.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.CountryTimeZones;
+import libcore.timezone.CountryTimeZones.TimeZoneMapping;
+import libcore.timezone.TimeZoneFinder;
+import libcore.timezone.ZoneInfoDB;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 4b8b7f3..af41b69 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -24,6 +24,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.DisplayCutout;
+import android.view.InsetsState;
import com.android.internal.os.IResultReceiver;
import android.util.MergedConfiguration;
@@ -53,6 +54,12 @@
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);
+
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 c836c9e..dace388 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();
/**
@@ -271,7 +272,7 @@
/**
* Called by the status bar to notify Views of changes to System UI visiblity.
*/
- oneway void statusBarVisibilityChanged(int visibility);
+ oneway void statusBarVisibilityChanged(int displayId, int visibility);
/**
* Called by System UI to notify of changes to the visibility of Recents.
@@ -294,9 +295,11 @@
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
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..7841d04
--- /dev/null
+++ b/core/java/android/view/InsetsController.java
@@ -0,0 +1,55 @@
+/*
+ * 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.Rect;
+
+import java.io.PrintWriter;
+
+/**
+ * Implements {@link WindowInsetsController} on the client.
+ */
+class InsetsController {
+
+ private final InsetsState mState = new InsetsState();
+ private final Rect mFrame = new Rect();
+
+ 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);
+ }
+
+ 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/media/java/android/media/update/ProviderCreator.java b/core/java/android/view/InsetsState.aidl
similarity index 67%
copy from media/java/android/media/update/ProviderCreator.java
copy to core/java/android/view/InsetsState.aidl
index f5f3e47..d02ddd1 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/core/java/android/view/InsetsState.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright 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.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package android.media.update;
+package android.view;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
-}
+parcelable InsetsState;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
new file mode 100644
index 0000000..9895adc
--- /dev/null
+++ b/core/java/android/view/InsetsState.java
@@ -0,0 +1,240 @@
+/*
+ * 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 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 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/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a7a5024..46f396a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -375,9 +375,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 +403,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 +537,10 @@
return this;
}
+ private boolean isColorLayerSet() {
+ return (mFlags & FX_SURFACE_DIM) == FX_SURFACE_DIM;
+ }
+
/**
* Indicates whether a 'ContainerLayer' is to be constructed.
*
@@ -550,6 +558,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 +881,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);
}
}
@@ -1427,7 +1439,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);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 2b68ec0..3c4ce8f 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();
@@ -1133,6 +1137,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 +1164,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 5f1336f..2767505 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3373,9 +3373,8 @@
*
* |-------|-------|-------|-------|
* 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
- * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT
- * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED
- * 1 PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE
+ * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
+ * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
* |-------|-------|-------|-------|
*/
@@ -3396,28 +3395,14 @@
* Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods
* should be called.
*
- * The idea is to call notifyNodeAdded() after the view is layout and visible, then call
- * notifyNodeRemoved() when it's gone (without known when it was removed from the parent).
- *
- * TODO(b/111276913): the current algortighm could probably be optimized and some of them
- * removed
+ * The idea is to call notifyAppeared() after the view is layout and visible, then call
+ * notifyDisappeared() when it's gone (without known when it was removed from the parent).
*/
- private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT = 0x10;
- private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED = 0x20;
- private static final int PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE = 0x40;
+ private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10;
+ private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20;
/* End of masks for mPrivateFlags4 */
- private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED = 1;
- private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED = 0;
-
- @IntDef(flag = true, prefix = { "CONTENT_CAPTURE_NOTIFICATION_TYPE_" }, value = {
- CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED,
- CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED
- })
- @Retention(RetentionPolicy.SOURCE)
- private @interface ContentCaptureNotificationType {}
-
/** @hide */
protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0;
/** @hide */
@@ -3993,7 +3978,7 @@
*
* @see #getParent()
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected ViewParent mParent;
/**
@@ -4199,7 +4184,7 @@
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mLeft;
/**
* The distance in pixels from the left edge of this view's parent
@@ -4207,7 +4192,7 @@
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mRight;
/**
* The distance in pixels from the top edge of this view's parent
@@ -4215,7 +4200,7 @@
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mTop;
/**
* The distance in pixels from the top edge of this view's parent
@@ -4223,7 +4208,7 @@
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mBottom;
/**
@@ -4714,7 +4699,7 @@
* of this view to at least this amount.
*/
@ViewDebug.ExportedProperty(category = "measurement")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mMinHeight;
/**
@@ -4722,7 +4707,7 @@
* of this view to at least this amount.
*/
@ViewDebug.ExportedProperty(category = "measurement")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mMinWidth;
/**
@@ -8152,6 +8137,9 @@
* the user, and the activity rendering the view is enabled for Content Capture) is laid out and
* is visible.
*
+ * <p>The populated structure is then passed to the service through
+ * {@link IntelligenceManager#notifyViewAppeared(ViewStructure)}.
+ *
* <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
* <ul>
* <li>{@link ViewStructure#setChildCount(int)}
@@ -8165,16 +8153,9 @@
* <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}
* <li>{@link ViewStructure#setDataIsSensitive(boolean)}
* </ul>
- *
- * @return whether the IntelligenceService should be notified that the view was added (through
- * the {@link IntelligenceManager#notifyViewAppeared(ViewStructure)} method) to the view
- * hierarchy. Most views should return {@code true} here, but views that contains virtual
- * hierarchy might opt to return {@code false} and notify the manager independently, as the
- * virtual views are rendered.
*/
- public boolean onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
+ public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
- return true;
}
/** @hide */
@@ -8937,65 +8918,61 @@
* Helper used to notify the {@link IntelligenceManager} 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.
+ *
+ * <p>This method is called from many places (visibility changed, view laid out, view attached
+ * or detached to/from window, etc...) and hence must contain the logic to call the manager, as
+ * described below:
+ *
+ * <ol>
+ * <li>It should only be called when content capture is enabled for the view.
+ * <li>It must call viewAppeared() before viewDisappeared()
+ * <li>viewAppearead() can only be called when the view is visible and laidout
+ * <li>It should not call the same event twice.
+ * </ol>
*/
- // TODO(b/111276913): make sure the current algorithm covers all cases. For example, it should
- // probably be called every time notifyEnterOrExitForAutoFillIfNeeded() is called as well.
- private void notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
- @ContentCaptureNotificationType int type) {
- if (type != CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
- && type != CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
- // Sanity check so it does not screw up the flags
- Log.wtf(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded(): "
- + "invalid type " + type + " for " + this);
- return;
- }
-
- if (!isImportantForContentCapture()) return;
+ private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
if (im == null || !im.isContentCaptureEnabled()) return;
- // Make sure event is notified just once, and reset the
- // PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE flag
- boolean ignoreNotification = false;
- if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
- if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
- == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
- ignoreNotification = true;
- } else {
- mPrivateFlags4 |= PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
- }
- } else {
- if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
- == CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
- ignoreNotification = true;
- } else {
- mPrivateFlags4 &= ~PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
- }
- }
- if (ignoreNotification) {
- if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
- // TODO(b/111276913): remove this log statement if the algorithm is not improved
- // (right now it's called too many times when the activity is stopped and/or views
- // disappear
- Log.v(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded("
- + type + "): ignoring repeated notification on " + this);
- }
- return;
- }
+ // NOTE: isImportantForContentCapture() is more expensive than im.isContentCaptureEnabled()
+ if (!isImportantForContentCapture()) return;
- if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
+ if (appeared) {
+ if (!isLaidOut() || !isVisibleToUser()
+ || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
+ + isLaidOut() + ", visible=" + isVisibleToUser()
+ + ": alreadyNotifiedAppeared="
+ + ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0));
+ }
+ return;
+ }
+ // All good: notify the manager...
final ViewStructure structure = im.newViewStructure(this);
- boolean notifyMgr = onProvideContentCaptureStructure(structure, /* flags= */ 0);
- if (notifyMgr) {
- im.notifyViewAppeared(structure);
- }
- mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED;
+ onProvideContentCaptureStructure(structure, /* flags= */ 0);
+ im.notifyViewAppeared(structure);
+ // ...and set the flags
+ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
+ mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
} else {
- if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED) == 0) {
- return; // skip initial notification
+ if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
+ || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this
+ + ": notifiedAppeared="
+ + ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
+ + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
+ & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
+ }
+ return;
}
+ // All good: notify the manager...
im.notifyViewDisappeared(getAutofillId());
+ // ...and set the flags
+ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+ mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
}
}
@@ -12902,6 +12879,7 @@
public void dispatchStartTemporaryDetach() {
mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
notifyEnterOrExitForAutoFillIfNeeded(false);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
onStartTemporaryDetach();
}
@@ -12928,6 +12906,7 @@
notifyFocusChangeToInputMethodManager(true /* hasFocus */);
}
notifyEnterOrExitForAutoFillIfNeeded(true);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
}
/**
@@ -13509,9 +13488,8 @@
: AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
}
}
- notifyNodeAddedOrRemovedForContentCaptureIfNeeded(isVisible
- ? CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
- : CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
+
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
}
/**
@@ -19082,6 +19060,7 @@
needGlobalAttributesUpdate(false);
notifyEnterOrExitForAutoFillIfNeeded(true);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
}
@UnsupportedAppUsage
@@ -19131,8 +19110,7 @@
}
notifyEnterOrExitForAutoFillIfNeeded(false);
- notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
- CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
}
/**
@@ -21438,12 +21416,7 @@
notifyEnterOrExitForAutoFillIfNeeded(true);
}
- if ((mViewFlags & VISIBILITY_MASK) == VISIBLE
- && (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT) == 0) {
- notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
- CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED);
- mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT;
- }
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
}
private boolean hasParentWantsFocus() {
@@ -21552,10 +21525,18 @@
}
/**
- * 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) {
setFrame(left, top, right, bottom);
}
@@ -23157,26 +23138,24 @@
* Modifies the input matrix such that it maps view-local coordinates to
* on-screen coordinates.
*
- * @param m input matrix to modify
- * @hide
+ * @param matrix input matrix to modify
*/
- @UnsupportedAppUsage
- public void transformMatrixToGlobal(Matrix m) {
+ public void transformMatrixToGlobal(Matrix matrix) {
final ViewParent parent = mParent;
if (parent instanceof View) {
final View vp = (View) parent;
- vp.transformMatrixToGlobal(m);
- m.preTranslate(-vp.mScrollX, -vp.mScrollY);
+ vp.transformMatrixToGlobal(matrix);
+ matrix.preTranslate(-vp.mScrollX, -vp.mScrollY);
} else if (parent instanceof ViewRootImpl) {
final ViewRootImpl vr = (ViewRootImpl) parent;
- vr.transformMatrixToGlobal(m);
- m.preTranslate(0, -vr.mCurScrollY);
+ vr.transformMatrixToGlobal(matrix);
+ matrix.preTranslate(0, -vr.mCurScrollY);
}
- m.preTranslate(mLeft, mTop);
+ matrix.preTranslate(mLeft, mTop);
if (!hasIdentityMatrix()) {
- m.preConcat(getMatrix());
+ matrix.preConcat(getMatrix());
}
}
@@ -23184,26 +23163,24 @@
* Modifies the input matrix such that it maps on-screen coordinates to
* view-local coordinates.
*
- * @param m input matrix to modify
- * @hide
+ * @param matrix input matrix to modify
*/
- @UnsupportedAppUsage
- public void transformMatrixToLocal(Matrix m) {
+ public void transformMatrixToLocal(Matrix matrix) {
final ViewParent parent = mParent;
if (parent instanceof View) {
final View vp = (View) parent;
- vp.transformMatrixToLocal(m);
- m.postTranslate(vp.mScrollX, vp.mScrollY);
+ vp.transformMatrixToLocal(matrix);
+ matrix.postTranslate(vp.mScrollX, vp.mScrollY);
} else if (parent instanceof ViewRootImpl) {
final ViewRootImpl vr = (ViewRootImpl) parent;
- vr.transformMatrixToLocal(m);
- m.postTranslate(0, vr.mCurScrollY);
+ vr.transformMatrixToLocal(matrix);
+ matrix.postTranslate(0, vr.mCurScrollY);
}
- m.postTranslate(-mLeft, -mTop);
+ matrix.postTranslate(-mLeft, -mTop);
if (!hasIdentityMatrix()) {
- m.postConcat(getInverseMatrix());
+ matrix.postConcat(getInverseMatrix());
}
}
@@ -24772,7 +24749,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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 484c6f3..d9d52c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -161,6 +161,19 @@
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";
+ private 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 +445,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 +459,7 @@
final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
boolean mPendingAlwaysConsumeNavBar;
+ private InsetsState mPendingInsets;
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
@@ -531,6 +547,8 @@
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
+ private final InsetsController mInsetsController = new InsetsController();
+
static final class SystemUiVisibilityInfo {
int seq;
int globalVisibility;
@@ -797,9 +815,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 +846,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 +1494,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 +1518,7 @@
*/
private void updateBoundsSurface() {
if (mBoundsSurfaceControl != null && mSurface.isValid()) {
- setBoundsSurfaceSizeAndCrop();
+ setBoundsSurfaceCrop();
mTransaction.deferTransactionUntilSurface(mBoundsSurfaceControl,
mSurface, mSurface.getNextFrameNumber())
.apply();
@@ -1780,7 +1792,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 +1810,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;
}
@@ -2000,6 +2019,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 +2215,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 +2254,10 @@
mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
contentInsetsChanged = true;
}
+ if (insetsStateChanged) {
+ mInsetsController.setState(mPendingInsets);
+ contentInsetsChanged = true;
+ }
if (contentInsetsChanged || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
|| mLastOverscanRequested != mAttachInfo.mOverscanRequested
@@ -2675,7 +2703,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 +4207,7 @@
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;
final class ViewRootHandler extends Handler {
@Override
@@ -4235,6 +4263,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 +4345,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 +4368,25 @@
requestLayout();
}
break;
+ case MSG_INSETS_CHANGED:
+ mPendingInsets = (InsetsState) msg.obj;
+
+ // TODO: Full traversal not 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 +6772,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 +6784,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 +6902,8 @@
mChoreographer.dump(prefix, writer);
+ mInsetsController.dump(prefix, writer);
+
writer.print(prefix); writer.println("View Hierarchy:");
dumpViewHierarchy(innerPrefix, writer, mView);
}
@@ -7064,6 +7112,10 @@
mHandler.sendMessage(msg);
}
+ private void dispatchInsetsChanged(InsetsState insetsState) {
+ mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).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 +8179,14 @@
}
@Override
+ public void insetsChanged(InsetsState insetsState) {
+ final ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchInsetsChanged(insetsState);
+ }
+ }
+
+ @Override
public void moved(int newX, int newY) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d4c7069..9227249 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -202,6 +202,14 @@
public static final String EXTRA_RESTORE_SESSION_TOKEN =
"android.view.autofill.extra.RESTORE_SESSION_TOKEN";
+ /**
+ * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
+ "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
+
private static final String SESSION_ID_TAG = "android:sessionId";
private static final String STATE_TAG = "android:state";
private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
@@ -370,6 +378,9 @@
private Cleaner mServiceClientCleaner;
@GuardedBy("mLock")
+ private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient;
+
+ @GuardedBy("mLock")
private AutofillCallback mCallback;
private final Context mContext;
@@ -1664,6 +1675,8 @@
final IAutoFillManager service = mService;
final IAutoFillManagerClient serviceClient = mServiceClient;
mServiceClientCleaner = Cleaner.create(this, () -> {
+ // TODO(b/111330312): call service to also remove reference to
+ // mAugmentedAutofillServiceClient
try {
service.removeClient(serviceClient, userId);
} catch (RemoteException e) {
@@ -1808,6 +1821,7 @@
if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
// Reset connection to system
mServiceClient = null;
+ mAugmentedAutofillServiceClient = null;
if (mServiceClientCleaner != null) {
mServiceClientCleaner.clean();
mServiceClientCleaner = null;
@@ -2054,6 +2068,29 @@
}
}
+ /**
+ * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
+ *
+ * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
+ * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
+ */
+ private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
+ synchronized (mLock) {
+ if (mAugmentedAutofillServiceClient == null) {
+ mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
+ }
+ final Bundle resultData = new Bundle();
+ resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
+ mAugmentedAutofillServiceClient.asBinder());
+
+ try {
+ result.send(0, resultData);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
+ }
+ }
+ }
+
/** @hide */
public void requestHideFillUi() {
requestHideFillUi(mIdShownFillUi, true);
@@ -2801,7 +2838,7 @@
private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
private final WeakReference<AutofillManager> mAfm;
- AutofillManagerClient(AutofillManager autofillManager) {
+ private AutofillManagerClient(AutofillManager autofillManager) {
mAfm = new WeakReference<>(autofillManager);
}
@@ -2904,6 +2941,50 @@
afm.post(() -> afm.setSessionFinished(newState));
}
}
+
+ @Override
+ public void getAugmentedAutofillClient(IResultReceiver result) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.getAugmentedAutofillClient(result));
+ }
+ }
+ }
+
+ private static final class AugmentedAutofillManagerClient
+ extends IAugmentedAutofillManagerClient.Stub {
+ private final WeakReference<AutofillManager> mAfm;
+
+ private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
+ mAfm = new WeakReference<>(autofillManager);
+ }
+
+ @Override
+ public Rect getViewCoordinates(@NonNull AutofillId id) {
+ // TODO(b/111330312): use handler / callback?
+ final AutofillManager afm = mAfm.get();
+ if (afm == null) return null;
+
+ final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id);
+ // TODO(b/111330312): optimize (for example, use temp rect from attach info) and
+ // fix (for example, take system status bar height into account) logic below
+ final int[] location = new int[2];
+ view.getLocationOnScreen(location);
+ final Rect rect = new Rect(location[0], location[1], location[0] + view.getWidth(),
+ location[1] + view.getHeight());
+ if (sVerbose) {
+ Log.v(TAG, "Coordinates for " + id + ": " + rect);
+ }
+ return rect;
+ }
+
+ @Override
+ public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.autofill(sessionId, ids, values));
+ }
+ }
}
/**
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
new file mode 100644
index 0000000..67cd0bf
--- /dev/null
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.autofill;
+
+import java.util.List;
+
+import android.graphics.Rect;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Object running in the application process and responsible to provide the functionalities
+ * required by an Augmented Autofill service.
+ *
+ * @hide
+ */
+interface IAugmentedAutofillManagerClient {
+ Rect getViewCoordinates(in AutofillId id);
+ void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 0ff7a0b..63394b4 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -27,6 +27,8 @@
import android.view.autofill.IAutofillWindowPresenter;
import android.view.KeyEvent;
+import com.android.internal.os.IResultReceiver;
+
/**
* Object running in the application process and responsible for autofilling it.
*
@@ -93,8 +95,18 @@
/**
* Marks the state of the session as finished.
+ *
* @param newState STATE_FINISHED (because the autofill service returned a null
* FillResponse) or STATE_UNKNOWN (because the session was removed).
*/
void setSessionFinished(int newState);
+
+ /**
+ * Gets a reference to the binder object that can be used by the Augmented Autofill service.
+ *
+ * @param receiver, whose AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT extra will contain
+ * the reference.
+ */
+ void getAugmentedAutofillClient(in IResultReceiver result);
+
}
diff --git a/core/java/android/view/intelligence/IIntelligenceManager.aidl b/core/java/android/view/intelligence/IIntelligenceManager.aidl
index 2f128de..7518ff5 100644
--- a/core/java/android/view/intelligence/IIntelligenceManager.aidl
+++ b/core/java/android/view/intelligence/IIntelligenceManager.aidl
@@ -38,7 +38,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
index dfa52d9..2f3b4ef 100644
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -39,7 +39,6 @@
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;
@@ -47,10 +46,18 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* TODO(b/111276913): add javadocs / implement
*/
+/*
+ * 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.
+ */
@SystemService(Context.INTELLIGENCE_MANAGER_SERVICE)
public final class IntelligenceManager {
@@ -97,48 +104,48 @@
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.
+ * Maximum number of events that are buffered before sent to the app.
*/
- private static final int MAX_DELAYED_SIZE = 20;
+ // 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;
- private final Object mLock = new Object();
-
@Nullable
- @GuardedBy("mLock")
private InteractionSessionId mId;
- @GuardedBy("mLock")
private int mState = STATE_UNKNOWN;
- @GuardedBy("mLock")
+ @Nullable
private IBinder mApplicationToken;
- // TODO(b/111276913): replace by an interface name implemented by Activity, similar to
- // AutofillClient
- @GuardedBy("mLock")
+ @Nullable
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<>();
+ @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 IntelligenceManager(@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();
@@ -149,102 +156,100 @@
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;
+ mHandler.sendMessage(obtainMessage(IntelligenceManager::handleStartSession, this,
+ token, componentName));
+ }
- if (VERBOSE) {
- Log.v(TAG, "onActivityCreated(): token=" + token + ", act="
- + getActivityDebugNameLocked() + ", id=" + mId);
- }
- final int flags = 0; // TODO(b/111276913): get proper flags
+ 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;
- 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();
- }
+ 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);
}
}
- //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 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) {
-
- //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;
+ 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 (mId == null) {
- // Sanity check - should not happen
- Log.wtf(TAG, "null session id for " + mComponentName);
- 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;
+ }
- //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();
+ 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);
}
}
@@ -256,41 +261,54 @@
public void onActivityLifecycleEvent(@EventType int type) {
if (!isContentCaptureEnabled()) return;
if (VERBOSE) {
- Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugNameLocked()
+ Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugName()
+ ": " + ContentCaptureEvent.getTypeAsString(type));
}
- sendEvent(new ContentCaptureEvent(type));
+ mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this,
+ new ContentCaptureEvent(type), /* forceFlush= */ true));
}
/** @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
+ //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);
+ }
- if (VERBOSE) {
- Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
- + ", mId=" + mId);
+ mHandler.sendMessage(obtainMessage(IntelligenceManager::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());
}
- try {
- mService.finishSession(mContext.getUserId(), mId);
- resetStateLocked();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mService.finishSession(mContext.getUserId(), mId, mEvents);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error finishing session " + mId + " for " + getActivityDebugName()
+ + ": " + e);
+ } finally {
+ handleResetState();
}
}
- @GuardedBy("mLock")
- private void resetStateLocked() {
+ private void handleResetState() {
mState = STATE_UNKNOWN;
mId = null;
mApplicationToken = null;
mComponentName = null;
- mEvents.clear();
+ mEvents = null;
}
/**
@@ -309,8 +327,11 @@
if (!(node instanceof ViewNode.ViewStructureImpl)) {
throw new IllegalArgumentException("Invalid node class: " + node.getClass());
}
- sendEvent(new ContentCaptureEvent(TYPE_VIEW_APPEARED)
- .setViewNode(((ViewNode.ViewStructureImpl) node).mNode));
+
+ mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this,
+ new ContentCaptureEvent(TYPE_VIEW_APPEARED)
+ .setViewNode(((ViewNode.ViewStructureImpl) node).mNode),
+ /* forceFlush= */ false));
}
/**
@@ -325,7 +346,9 @@
Preconditions.checkNotNull(id);
if (!isContentCaptureEnabled()) return;
- sendEvent(new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id));
+ mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this,
+ new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id),
+ /* forceFlush= */ false));
}
/**
@@ -339,10 +362,12 @@
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));
+ mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this,
+ new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+ .setText(text), /* forceFlush= */ false));
}
/**
@@ -384,14 +409,11 @@
* 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;
+ return mService != null && !mDisabled.get();
}
/**
- * Called by apps to explicitly enabled or disable content capture.
+ * 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)}.
@@ -504,25 +526,36 @@
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("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("user: "); pw.println(mContext.getUserId());
- pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled());
+ }
+ 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(")");
+ }
+ 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 == null ? "null" : mComponentName.flattenToShortString());
+ pw.println(mComponentName.flattenToShortString());
+ }
+ if (mEvents != null) {
final int numberEvents = mEvents.size();
- pw.print(prefix2); pw.print("batched events: "); pw.println(numberEvents);
- if (numberEvents > 0) {
+ pw.print(prefix2); pw.print("batched 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.println(i); pw.print(": "); event.dump(pw); pw.println();
+ pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+ pw.println();
}
-
}
}
}
@@ -530,8 +563,7 @@
/**
* Gets a string that can be used to identify the activity on logging statements.
*/
- @GuardedBy("mLock")
- private String getActivityDebugNameLocked() {
+ private String getActivityDebugName() {
return mComponentName == null ? mContext.getPackageName()
: mComponentName.flattenToShortString();
}
diff --git a/core/java/android/view/intelligence/ViewNode.java b/core/java/android/view/intelligence/ViewNode.java
index cc78e6b..ea57461 100644
--- a/core/java/android/view/intelligence/ViewNode.java
+++ b/core/java/android/view/intelligence/ViewNode.java
@@ -238,6 +238,8 @@
@Override
public void setText(CharSequence text, int selectionStart, int selectionEnd) {
+ // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
+ mNode.mText = text;
// TODO(b/111276913): implement or move to superclass
}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
new file mode 100644
index 0000000..8df83c0
--- /dev/null
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -0,0 +1,115 @@
+/*
+ * 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.textclassifier;
+
+import android.annotation.NonNull;
+import android.app.Person;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.android.textclassifier.ActionsSuggestionsModel;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Helper class for action suggestions.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class ActionsSuggestionsHelper {
+ private static final int USER_LOCAL = 0;
+ private static final int FIRST_NON_LOCAL_USER = 1;
+
+ private ActionsSuggestionsHelper() {}
+
+ /**
+ * Converts the messages to a list of native messages object that the model can understand.
+ * <p>
+ * User id encoding - local user is represented as 0, Other users are numbered according to
+ * how far before they spoke last time in the conversation. For example, considering this
+ * conversation:
+ * <ul>
+ * <li> User A: xxx
+ * <li> Local user: yyy
+ * <li> User B: zzz
+ * </ul>
+ * User A will be encoded as 2, user B will be encoded as 1 and local user will be encoded as 0.
+ */
+ @NonNull
+ public static ActionsSuggestionsModel.ConversationMessage[] toNativeMessages(
+ @NonNull List<ConversationActions.Message> messages) {
+ List<ConversationActions.Message> messagesWithText =
+ messages.stream()
+ .filter(message -> !TextUtils.isEmpty(message.getText()))
+ .collect(Collectors.toCollection(ArrayList::new));
+ if (messagesWithText.isEmpty()) {
+ return new ActionsSuggestionsModel.ConversationMessage[0];
+ }
+ int size = messagesWithText.size();
+ // If the last message (the most important one) does not have the Person object, we will
+ // just use the last message and consider this message is sent from a remote user.
+ ConversationActions.Message lastMessage = messages.get(size - 1);
+ boolean useLastMessageOnly = lastMessage.getAuthor() == null;
+ if (useLastMessageOnly) {
+ return new ActionsSuggestionsModel.ConversationMessage[]{
+ new ActionsSuggestionsModel.ConversationMessage(
+ FIRST_NON_LOCAL_USER,
+ lastMessage.getText().toString())};
+ }
+
+ // Encode the messages in the reverse order, stop whenever the Person object is missing.
+ Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
+ PersonEncoder personEncoder = new PersonEncoder();
+ for (int i = size - 1; i >= 0; i--) {
+ ConversationActions.Message message = messagesWithText.get(i);
+ if (message.getAuthor() == null) {
+ break;
+ }
+ nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
+ personEncoder.encode(message.getAuthor()),
+ message.getText().toString()));
+ }
+ return nativeMessages.toArray(
+ new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
+ }
+
+ private static final class PersonEncoder {
+ private final Map<Person, Integer> mMapping = new ArrayMap<>();
+ private int mNextUserId = FIRST_NON_LOCAL_USER;
+
+ private int encode(Person person) {
+ if (ConversationActions.Message.PERSON_USER_LOCAL.equals(person)) {
+ return USER_LOCAL;
+ }
+ Integer result = mMapping.get(person);
+ if (result == null) {
+ mMapping.put(person, mNextUserId);
+ result = mNextUserId;
+ mNextUserId++;
+ }
+ return result;
+ }
+ }
+}
diff --git a/media/java/android/media/update/ProviderCreator.java b/core/java/android/view/textclassifier/ConversationActions.aidl
similarity index 74%
rename from media/java/android/media/update/ProviderCreator.java
rename to core/java/android/view/textclassifier/ConversationActions.aidl
index f5f3e47..fece939 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/core/java/android/view/textclassifier/ConversationActions.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,7 @@
* limitations under the License.
*/
-package android.media.update;
+package android.view.textclassifier;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
-}
+parcelable ConversationActions;
+parcelable ConversationActions.Request;
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 5fcf227..1a7b911 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -345,6 +345,16 @@
/** Represents a message in the conversation. */
public static final class Message implements Parcelable {
+ /**
+ * Represents the local user.
+ *
+ * @see Builder#setAuthor(Person)
+ */
+ public static final Person PERSON_USER_LOCAL =
+ new Person.Builder()
+ .setKey("text-classifier-conversation-actions-local-user")
+ .build();
+
@Nullable
private final Person mAuthor;
@Nullable
@@ -446,7 +456,11 @@
@Nullable
private Bundle mExtras;
- /** Sets the person who composed this message. */
+ /**
+ * Sets the person who composed this message.
+ * <p>
+ * Use {@link #PERSON_USER_LOCAL} to represent the local user.
+ */
@NonNull
public Builder setAuthor(@Nullable Person author) {
mAuthor = author;
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 16eb5af..f8fce62 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -23,8 +23,10 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.service.textclassifier.IConversationActionsCallback;
import android.service.textclassifier.ITextClassificationCallback;
import android.service.textclassifier.ITextClassifierService;
+import android.service.textclassifier.ITextLanguageCallback;
import android.service.textclassifier.ITextLinksCallback;
import android.service.textclassifier.ITextSelectionCallback;
@@ -76,7 +78,7 @@
if (selection != null) {
return selection;
}
- } catch (RemoteException | InterruptedException e) {
+ } catch (RemoteException e) {
Log.e(LOG_TAG, "Error suggesting selection for text. Using fallback.", e);
}
return mFallback.suggestSelection(request);
@@ -97,7 +99,7 @@
if (classification != null) {
return classification;
}
- } catch (RemoteException | InterruptedException e) {
+ } catch (RemoteException e) {
Log.e(LOG_TAG, "Error classifying text. Using fallback.", e);
}
return mFallback.classifyText(request);
@@ -124,7 +126,7 @@
if (links != null) {
return links;
}
- } catch (RemoteException | InterruptedException e) {
+ } catch (RemoteException e) {
Log.e(LOG_TAG, "Error generating links. Using fallback.", e);
}
return mFallback.generateLinks(request);
@@ -142,6 +144,42 @@
}
}
+ @Override
+ public TextLanguage detectLanguage(TextLanguage.Request request) {
+ Preconditions.checkNotNull(request);
+ Utils.checkMainThread();
+
+ try {
+ final TextLanguageCallback callback = new TextLanguageCallback();
+ mManagerService.onDetectLanguage(mSessionId, request, callback);
+ final TextLanguage textLanguage = callback.mReceiver.get();
+ if (textLanguage != null) {
+ return textLanguage;
+ }
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Error detecting language.", e);
+ }
+ return mFallback.detectLanguage(request);
+ }
+
+ @Override
+ public ConversationActions suggestConversationActions(ConversationActions.Request request) {
+ Preconditions.checkNotNull(request);
+ Utils.checkMainThread();
+
+ try {
+ final ConversationActionsCallback callback = new ConversationActionsCallback();
+ mManagerService.onSuggestConversationActions(mSessionId, request, callback);
+ final ConversationActions conversationActions = callback.mReceiver.get();
+ if (conversationActions != null) {
+ return conversationActions;
+ }
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Error reporting selection event.", e);
+ }
+ return mFallback.suggestConversationActions(request);
+ }
+
/**
* @inheritDoc
*/
@@ -193,7 +231,7 @@
private static final class TextSelectionCallback extends ITextSelectionCallback.Stub {
- final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>();
+ final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>("textselection");
@Override
public void onSuccess(TextSelection selection) {
@@ -208,7 +246,8 @@
private static final class TextClassificationCallback extends ITextClassificationCallback.Stub {
- final ResponseReceiver<TextClassification> mReceiver = new ResponseReceiver<>();
+ final ResponseReceiver<TextClassification> mReceiver =
+ new ResponseReceiver<>("textclassification");
@Override
public void onSuccess(TextClassification classification) {
@@ -223,7 +262,7 @@
private static final class TextLinksCallback extends ITextLinksCallback.Stub {
- final ResponseReceiver<TextLinks> mReceiver = new ResponseReceiver<>();
+ final ResponseReceiver<TextLinks> mReceiver = new ResponseReceiver<>("textlinks");
@Override
public void onSuccess(TextLinks links) {
@@ -236,12 +275,48 @@
}
}
+ private static final class TextLanguageCallback extends ITextLanguageCallback.Stub {
+
+ final ResponseReceiver<TextLanguage> mReceiver = new ResponseReceiver<>("textlanguage");
+
+ @Override
+ public void onSuccess(TextLanguage textLanguage) {
+ mReceiver.onSuccess(textLanguage);
+ }
+
+ @Override
+ public void onFailure() {
+ mReceiver.onFailure();
+ }
+ }
+
+ private static final class ConversationActionsCallback
+ extends IConversationActionsCallback.Stub {
+
+ final ResponseReceiver<ConversationActions> mReceiver =
+ new ResponseReceiver<>("conversationaction");
+
+ @Override
+ public void onSuccess(ConversationActions conversationActions) {
+ mReceiver.onSuccess(conversationActions);
+ }
+
+ @Override
+ public void onFailure() {
+ mReceiver.onFailure();
+ }
+ }
+
private static final class ResponseReceiver<T> {
private final CountDownLatch mLatch = new CountDownLatch(1);
-
+ private final String mName;
private T mResponse;
+ private ResponseReceiver(String name) {
+ mName = name;
+ }
+
public void onSuccess(T response) {
mResponse = response;
mLatch.countDown();
@@ -253,13 +328,21 @@
}
@Nullable
- public T get() throws InterruptedException {
+ public T get() {
// If this is running on the main thread, do not block for a response.
// The response will unfortunately be null and the TextClassifier should depend on its
// fallback.
// NOTE that TextClassifier calls should preferably always be called on a worker thread.
if (Looper.myLooper() != Looper.getMainLooper()) {
- mLatch.await(2, TimeUnit.SECONDS);
+ try {
+ boolean success = mLatch.await(2, TimeUnit.SECONDS);
+ if (!success) {
+ Log.w(LOG_TAG, "Timeout in ResponseReceiver.get(): " + mName);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.e(LOG_TAG, "Interrupted during ResponseReceiver.get(): " + mName, e);
+ }
}
return mResponse;
}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index f6c3d77..e0910c0 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -105,7 +105,7 @@
/**
* @hide
*/
- static final TextClassification EMPTY = new TextClassification.Builder().build();
+ public static final TextClassification EMPTY = new TextClassification.Builder().build();
private static final String LOG_TAG = "TextClassification";
// TODO(toki): investigate a way to derive this based on device properties.
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 524f709..a2536cb 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -90,6 +90,11 @@
String TYPE_DATE_TIME = "datetime";
/** Flight number in IATA format. */
String TYPE_FLIGHT_NUMBER = "flight";
+ /**
+ * Word that users may be interested to look up for meaning.
+ * @hide
+ */
+ String TYPE_DICTIONARY = "dictionary";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -103,6 +108,7 @@
TYPE_DATE,
TYPE_DATE_TIME,
TYPE_FLIGHT_NUMBER,
+ TYPE_DICTIONARY
})
@interface EntityType {}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 798a820..8e14dfd 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -40,9 +40,9 @@
import android.provider.Browser;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
-import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -269,17 +269,17 @@
final ZonedDateTime refTime = ZonedDateTime.now();
final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
? request.getEntityConfig().resolveEntityListModifications(
- getEntitiesForHints(request.getEntityConfig().getHints()))
+ getEntitiesForHints(request.getEntityConfig().getHints()))
: mSettings.getEntityListDefault();
final AnnotatorModel annotatorImpl =
getAnnotatorImpl(request.getDefaultLocales());
final AnnotatorModel.AnnotatedSpan[] annotations =
annotatorImpl.annotate(
- textString,
- new AnnotatorModel.AnnotationOptions(
- refTime.toInstant().toEpochMilli(),
- refTime.getZone().getId(),
- concatenateLocales(request.getDefaultLocales())));
+ textString,
+ new AnnotatorModel.AnnotationOptions(
+ refTime.toInstant().toEpochMilli(),
+ refTime.getZone().getId(),
+ concatenateLocales(request.getDefaultLocales())));
for (AnnotatorModel.AnnotatedSpan span : annotations) {
final AnnotatorModel.ClassificationResult[] results =
span.getClassification();
@@ -373,20 +373,13 @@
// Actions model is optional, fallback if it is not available.
return mFallback.suggestConversationActions(request);
}
- List<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayList<>();
- for (ConversationActions.Message message : request.getConversation()) {
- if (TextUtils.isEmpty(message.getText())) {
- continue;
- }
- // TODO: We need to map the Person object to user id.
- int userId = 1;
- nativeMessages.add(
- new ActionsSuggestionsModel.ConversationMessage(
- userId, message.getText().toString()));
+ ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
+ ActionsSuggestionsHelper.toNativeMessages(request.getConversation());
+ if (nativeMessages.length == 0) {
+ return mFallback.suggestConversationActions(request);
}
ActionsSuggestionsModel.Conversation nativeConversation =
- new ActionsSuggestionsModel.Conversation(nativeMessages.toArray(
- new ActionsSuggestionsModel.ConversationMessage[0]));
+ new ActionsSuggestionsModel.Conversation(nativeMessages);
ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions =
actionsImpl.suggestActions(nativeConversation, null);
@@ -523,10 +516,10 @@
final TextClassification.Builder builder = new TextClassification.Builder()
.setText(classifiedText);
- final int size = classifications.length;
+ final int typeCount = classifications.length;
AnnotatorModel.ClassificationResult highestScoringResult =
- size > 0 ? classifications[0] : null;
- for (int i = 0; i < size; i++) {
+ typeCount > 0 ? classifications[0] : null;
+ for (int i = 0; i < typeCount; i++) {
builder.setEntityType(classifications[i].getCollection(),
classifications[i].getScore());
if (classifications[i].getScore() > highestScoringResult.getScore()) {
@@ -534,9 +527,12 @@
}
}
+ // TODO: Make this configurable.
+ final float foreignTextThreshold = typeCount == 0 ? 0.5f : 0.7f;
boolean isPrimaryAction = true;
for (LabeledIntent labeledIntent : IntentFactory.create(
- mContext, classifiedText, referenceTime, highestScoringResult)) {
+ mContext, classifiedText, isForeignText(classifiedText, foreignTextThreshold),
+ referenceTime, highestScoringResult)) {
final RemoteAction action = labeledIntent.asRemoteAction(mContext);
if (action == null) {
continue;
@@ -558,6 +554,42 @@
return builder.setId(createId(text, start, end)).build();
}
+ private boolean isForeignText(String text, float threshold) {
+ // TODO: Revisit this algorithm.
+ try {
+ final LangIdModel.LanguageResult[] langResults = getLangIdImpl().detectLanguages(text);
+ if (langResults.length <= 0) {
+ return false;
+ }
+
+ LangIdModel.LanguageResult highestScoringResult = langResults[0];
+ for (int i = 1; i < langResults.length; i++) {
+ if (langResults[i].getScore() > highestScoringResult.getScore()) {
+ highestScoringResult = langResults[i];
+ }
+ }
+ if (highestScoringResult.getScore() < threshold) {
+ return false;
+ }
+ // TODO: Remove
+ Log.d(LOG_TAG, String.format("Language detected: <%s:%s>",
+ highestScoringResult.getLanguage(), highestScoringResult.getScore()));
+
+ final Locale detected = new Locale(highestScoringResult.getLanguage());
+ final LocaleList deviceLocales = LocaleList.getDefault();
+ final int size = deviceLocales.size();
+ for (int i = 0; i < size; i++) {
+ if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) {
+ return false;
+ }
+ }
+ return true;
+ } catch (Throwable t) {
+ Log.e(LOG_TAG, "Error detecting foreign text. Ignored.", t);
+ }
+ return false;
+ }
+
@Override
public void dump(@NonNull IndentingPrintWriter printWriter) {
synchronized (mLock) {
@@ -608,7 +640,8 @@
/**
* Helper class to store the information from which RemoteActions are built.
*/
- private static final class LabeledIntent {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public static final class LabeledIntent {
static final int DEFAULT_REQUEST_CODE = 0;
@@ -643,7 +676,8 @@
return mDescription;
}
- Intent getIntent() {
+ @VisibleForTesting
+ public Intent getIntent() {
return mIntent;
}
@@ -687,7 +721,8 @@
/**
* Creates intents based on the classification type.
*/
- static final class IntentFactory {
+ @VisibleForTesting
+ public static final class IntentFactory {
private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
@@ -698,53 +733,70 @@
public static List<LabeledIntent> create(
Context context,
String text,
+ boolean foreignText,
@Nullable Instant referenceTime,
@Nullable AnnotatorModel.ClassificationResult classification) {
final String type = classification != null
? classification.getCollection().trim().toLowerCase(Locale.ENGLISH)
- : null;
+ : "";
text = text.trim();
+ final List<LabeledIntent> actions;
switch (type) {
case TextClassifier.TYPE_EMAIL:
- return createForEmail(context, text);
+ actions = createForEmail(context, text);
+ break;
case TextClassifier.TYPE_PHONE:
- return createForPhone(context, text);
+ actions = createForPhone(context, text);
+ break;
case TextClassifier.TYPE_ADDRESS:
- return createForAddress(context, text);
+ actions = createForAddress(context, text);
+ break;
case TextClassifier.TYPE_URL:
- return createForUrl(context, text);
- case TextClassifier.TYPE_DATE:
+ actions = createForUrl(context, text);
+ break;
+ case TextClassifier.TYPE_DATE: // fall through
case TextClassifier.TYPE_DATE_TIME:
if (classification.getDatetimeResult() != null) {
final Instant parsedTime = Instant.ofEpochMilli(
classification.getDatetimeResult().getTimeMsUtc());
- return createForDatetime(context, type, referenceTime, parsedTime);
+ actions = createForDatetime(context, type, referenceTime, parsedTime);
} else {
- return new ArrayList<>();
+ actions = new ArrayList<>();
}
+ break;
case TextClassifier.TYPE_FLIGHT_NUMBER:
- return createForFlight(context, text);
+ actions = createForFlight(context, text);
+ break;
+ case TextClassifier.TYPE_DICTIONARY:
+ actions = createForDictionary(context, text);
+ break;
default:
- return new ArrayList<>();
+ actions = new ArrayList<>();
+ break;
}
+ if (foreignText) {
+ insertTranslateAction(actions, context, text);
+ }
+ return actions;
}
@NonNull
private static List<LabeledIntent> createForEmail(Context context, String text) {
- return Arrays.asList(
- new LabeledIntent(
- context.getString(com.android.internal.R.string.email),
- context.getString(com.android.internal.R.string.email_desc),
- new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("mailto:%s", text))),
- LabeledIntent.DEFAULT_REQUEST_CODE),
- new LabeledIntent(
- context.getString(com.android.internal.R.string.add_contact),
- context.getString(com.android.internal.R.string.add_contact_desc),
- new Intent(Intent.ACTION_INSERT_OR_EDIT)
- .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
- text.hashCode()));
+ final List<LabeledIntent> actions = new ArrayList<>();
+ actions.add(new LabeledIntent(
+ context.getString(com.android.internal.R.string.email),
+ context.getString(com.android.internal.R.string.email_desc),
+ new Intent(Intent.ACTION_SENDTO)
+ .setData(Uri.parse(String.format("mailto:%s", text))),
+ LabeledIntent.DEFAULT_REQUEST_CODE));
+ actions.add(new LabeledIntent(
+ context.getString(com.android.internal.R.string.add_contact),
+ context.getString(com.android.internal.R.string.add_contact_desc),
+ new Intent(Intent.ACTION_INSERT_OR_EDIT)
+ .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+ .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
+ text.hashCode()));
+ return actions;
}
@NonNull
@@ -801,12 +853,14 @@
if (Uri.parse(text).getScheme() == null) {
text = "http://" + text;
}
- return Arrays.asList(new LabeledIntent(
+ final List<LabeledIntent> actions = new ArrayList<>();
+ actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.browse),
context.getString(com.android.internal.R.string.browse_desc),
new Intent(Intent.ACTION_VIEW, Uri.parse(text))
.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
LabeledIntent.DEFAULT_REQUEST_CODE));
+ return actions;
}
@NonNull
@@ -828,12 +882,14 @@
@NonNull
private static List<LabeledIntent> createForFlight(Context context, String text) {
- return Arrays.asList(new LabeledIntent(
+ final List<LabeledIntent> actions = new ArrayList<>();
+ actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.view_flight),
context.getString(com.android.internal.R.string.view_flight_desc),
new Intent(Intent.ACTION_WEB_SEARCH)
.putExtra(SearchManager.QUERY, text),
text.hashCode()));
+ return actions;
}
@NonNull
@@ -864,5 +920,27 @@
parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
parsedTime.hashCode());
}
+
+ private static void insertTranslateAction(
+ List<LabeledIntent> actions, Context context, String text) {
+ actions.add(new LabeledIntent(
+ context.getString(com.android.internal.R.string.translate),
+ context.getString(com.android.internal.R.string.translate_desc),
+ new Intent(Intent.ACTION_TRANSLATE)
+ // TODO: Probably better to introduce a "translate" scheme instead of
+ // using EXTRA_TEXT.
+ .putExtra(Intent.EXTRA_TEXT, text),
+ text.hashCode()));
+ }
+
+ @NonNull
+ private static List<LabeledIntent> createForDictionary(Context context, String text) {
+ return Arrays.asList(new LabeledIntent(
+ context.getString(com.android.internal.R.string.define),
+ context.getString(com.android.internal.R.string.define_desc),
+ new Intent(Intent.ACTION_DEFINE)
+ .putExtra(Intent.EXTRA_TEXT, text),
+ text.hashCode()));
+ }
}
}
diff --git a/media/java/android/media/update/ProviderCreator.java b/core/java/android/view/textclassifier/TextLanguage.aidl
similarity index 74%
copy from media/java/android/media/update/ProviderCreator.java
copy to core/java/android/view/textclassifier/TextLanguage.aidl
index f5f3e47..54e3cf9f 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/core/java/android/view/textclassifier/TextLanguage.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,7 @@
* limitations under the License.
*/
-package android.media.update;
+package android.view.textclassifier;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
-}
+parcelable TextLanguage;
+parcelable TextLanguage.Request;
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 02aee50..1e42c41 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -59,7 +59,7 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATUS_LINKS_APPLIED, STATUS_NO_LINKS_FOUND, STATUS_NO_LINKS_APPLIED,
- STATUS_DIFFERENT_TEXT})
+ STATUS_DIFFERENT_TEXT, STATUS_UNSUPPORTED_CHARACTER})
public @interface Status {}
/** Links were successfully applied to the text. */
@@ -74,6 +74,9 @@
/** The specified text does not match the text used to generate the links. */
public static final int STATUS_DIFFERENT_TEXT = 3;
+ /** The specified text contains unsupported characters. */
+ public static final int STATUS_UNSUPPORTED_CHARACTER = 4;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({APPLY_STRATEGY_IGNORE, APPLY_STRATEGY_REPLACE})
diff --git a/core/java/android/view/textclassifier/TextLinksParams.java b/core/java/android/view/textclassifier/TextLinksParams.java
index be4c3bc..8af4233 100644
--- a/core/java/android/view/textclassifier/TextLinksParams.java
+++ b/core/java/android/view/textclassifier/TextLinksParams.java
@@ -107,6 +107,13 @@
Preconditions.checkNotNull(textLinks);
final String textString = text.toString();
+
+ if (Linkify.containsUnsupportedCharacters(textString)) {
+ // Do not apply links to text containing unsupported characters.
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ return TextLinks.STATUS_UNSUPPORTED_CHARACTER;
+ }
+
if (!textString.startsWith(textLinks.getText())) {
return TextLinks.STATUS_DIFFERENT_TEXT;
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1093719..414cb8f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -410,6 +410,9 @@
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
}
+ if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
+ setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
+ }
if (context == null) {
throw new IllegalArgumentException("Invalid context argument");
@@ -2695,8 +2698,8 @@
}
@Override
- public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
- return mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
+ public void onProvideContentCaptureStructure(ViewStructure structure, int flags) {
+ mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
}
@Override
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index ceada07..95e7a986 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -341,10 +341,9 @@
return true; // true is the default value returned by View.isVisibleToUserForAutofill()
}
- default boolean onProvideContentCaptureStructure(
+ default void onProvideContentCaptureStructure(
@SuppressWarnings("unused") android.view.ViewStructure structure,
@SuppressWarnings("unused") int flags) {
- return false; // WebView provides virtual views and is responsible to notify manager
}
public AccessibilityNodeProvider getAccessibilityNodeProvider();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5b1544b..c6155ce 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -390,7 +390,8 @@
com.android.internal.R.bool.config_enableHapticTextHandle);
if (FLAG_USE_MAGNIFIER) {
- final Magnifier magnifier = new Magnifier.Builder(mTextView).build();
+ final Magnifier magnifier =
+ Magnifier.createBuilderWithOldMagnifierDefaults(mTextView).build();
mMagnifierAnimator = new MagnifierMotionAnimator(magnifier);
}
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 12cc54d..c21182c 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -106,9 +106,9 @@
private boolean mHaveFrame = false;
@UnsupportedAppUsage
private boolean mAdjustViewBounds = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mMaxWidth = Integer.MAX_VALUE;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mMaxHeight = Integer.MAX_VALUE;
// these are applied to the drawable
@@ -1331,9 +1331,17 @@
}
}
- /** @hide */
- @UnsupportedAppUsage
- public void animateTransform(Matrix matrix) {
+ /**
+ * Applies a temporary transformation {@link Matrix} to the view's drawable when it is drawn.
+ * Allows custom scaling, translation, and perspective distortion during an animation.
+ *
+ * This method is a lightweight analogue of {@link ImageView#setImageMatrix(Matrix)} to use
+ * only during animations as this matrix will be cleared after the next drawable
+ * update or view's bounds change.
+ *
+ * @param matrix The transformation parameters in matrix form.
+ */
+ public void animateTransform(@Nullable Matrix matrix) {
if (mDrawable == null) {
return;
}
@@ -1341,6 +1349,7 @@
final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
mDrawable.setBounds(0, 0, vwidth, vheight);
+ mDrawMatrix = null;
} else {
mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
if (mDrawMatrix == null) {
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index f2e478d..15910bb 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -189,7 +189,7 @@
@ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
}, formatToHexString = true)
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mGravity = Gravity.START | Gravity.TOP;
@ViewDebug.ExportedProperty(category = "measurement")
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 932f182..7d02757 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -145,7 +145,47 @@
*/
@Deprecated
public Magnifier(@NonNull View view) {
- this(new Builder(view));
+ this(createBuilderWithOldMagnifierDefaults(view));
+ }
+
+ static Builder createBuilderWithOldMagnifierDefaults(final View view) {
+ final Builder params = new Builder(view);
+ final Context context = view.getContext();
+ final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Magnifier,
+ R.attr.magnifierStyle, 0);
+ params.mWidth = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierWidth, 0);
+ params.mHeight = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHeight, 0);
+ params.mElevation = a.getDimension(R.styleable.Magnifier_magnifierElevation, 0);
+ params.mCornerRadius = getDeviceDefaultDialogCornerRadius(context);
+ params.mZoom = a.getFloat(R.styleable.Magnifier_magnifierZoom, 0);
+ params.mHorizontalDefaultSourceToMagnifierOffset =
+ a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHorizontalOffset, 0);
+ params.mVerticalDefaultSourceToMagnifierOffset =
+ a.getDimensionPixelSize(R.styleable.Magnifier_magnifierVerticalOffset, 0);
+ params.mOverlay = new ColorDrawable(a.getColor(
+ R.styleable.Magnifier_magnifierColorOverlay, Color.TRANSPARENT));
+ a.recycle();
+ params.mForcePositionWithinWindowSystemInsetsBounds = true;
+ params.mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
+ params.mTopContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
+ params.mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
+ params.mBottomContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
+ return params;
+ }
+
+ /**
+ * Returns the device default theme dialog corner radius attribute.
+ * We retrieve this from the device default theme to avoid
+ * using the values set in the custom application themes.
+ */
+ private static float getDeviceDefaultDialogCornerRadius(final Context context) {
+ final Context deviceDefaultContext =
+ new ContextThemeWrapper(context, R.style.Theme_DeviceDefault);
+ final TypedArray ta = deviceDefaultContext.obtainStyledAttributes(
+ new int[]{android.R.attr.dialogCornerRadius});
+ final float dialogCornerRadius = ta.getDimension(0, 0);
+ ta.recycle();
+ return dialogCornerRadius;
}
private Magnifier(@NonNull Builder params) {
@@ -797,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();
@@ -1105,41 +1145,23 @@
}
private void applyDefaults() {
- final Context context = mView.getContext();
- final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Magnifier,
- R.attr.magnifierStyle, 0);
- mWidth = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierWidth, 0);
- mHeight = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHeight, 0);
- mElevation = a.getDimension(R.styleable.Magnifier_magnifierElevation, 0);
- mCornerRadius = getDeviceDefaultDialogCornerRadius();
- mZoom = a.getFloat(R.styleable.Magnifier_magnifierZoom, 0);
+ final Resources resources = mView.getContext().getResources();
+ mWidth = resources.getDimensionPixelSize(R.dimen.default_magnifier_width);
+ mHeight = resources.getDimensionPixelSize(R.dimen.default_magnifier_height);
+ mElevation = resources.getDimension(R.dimen.default_magnifier_elevation);
+ mCornerRadius = resources.getDimension(R.dimen.default_magnifier_corner_radius);
+ mZoom = resources.getFloat(R.dimen.default_magnifier_zoom);
mHorizontalDefaultSourceToMagnifierOffset =
- a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHorizontalOffset, 0);
+ resources.getDimensionPixelSize(R.dimen.default_magnifier_horizontal_offset);
mVerticalDefaultSourceToMagnifierOffset =
- a.getDimensionPixelSize(R.styleable.Magnifier_magnifierVerticalOffset, 0);
- mOverlay = new ColorDrawable(a.getColor(
- R.styleable.Magnifier_magnifierColorOverlay, Color.TRANSPARENT));
- a.recycle();
+ resources.getDimensionPixelSize(R.dimen.default_magnifier_vertical_offset);
+ mOverlay = new ColorDrawable(resources.getColor(
+ R.color.default_magnifier_color_overlay, null));
mForcePositionWithinWindowSystemInsetsBounds = true;
mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
- mTopContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
+ mTopContentBound = SOURCE_BOUND_MAX_VISIBLE;
mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
- mBottomContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
- }
-
- /**
- * Returns the device default theme dialog corner radius attribute.
- * We retrieve this from the device default theme to avoid
- * using the values set in the custom application themes.
- */
- private float getDeviceDefaultDialogCornerRadius() {
- final Context deviceDefaultContext =
- new ContextThemeWrapper(mView.getContext(), R.style.Theme_DeviceDefault);
- final TypedArray ta = deviceDefaultContext.obtainStyledAttributes(
- new int[]{android.R.attr.dialogCornerRadius});
- final float dialogCornerRadius = ta.getDimension(0, 0);
- ta.recycle();
- return dialogCornerRadius;
+ mBottomContentBound = SOURCE_BOUND_MAX_VISIBLE;
}
/**
@@ -1186,8 +1208,7 @@
}
/**
- * Sets the corner radius of the magnifier window, in pixels.
- * Defaults to the corner radius defined in the device default theme.
+ * Sets the corner radius of the magnifier window, in pixels. Defaults to 2dp.
* @param cornerRadius the corner radius to be set
*/
@NonNull
@@ -1201,10 +1222,11 @@
/**
* Sets an overlay that will be drawn on the top of the magnifier content.
* In general, the overlay should not be opaque, in order to let the expected magnifier
- * content be partially visible. The default overlay is a white {@link ColorDrawable},
- * with 5% alpha, aiming to make the magnifier distinguishable when shown in dark
- * application regions. To disable this default (or in general to have no overlay), the
- * parameter should be set to {@code null}. The overlay will be automatically redrawn
+ * content be partially visible. The default overlay is {@code null} (no overlay).
+ * As an example, TextView applies a white {@link ColorDrawable} overlay with
+ * 5% alpha, aiming to make the magnifier distinguishable when shown in dark
+ * application regions. To disable the overlay, the parameter should be set
+ * to {@code null}. If not null, the overlay will be automatically redrawn
* when the drawable is invalidated. To achieve this, the magnifier will set a new
* {@link android.graphics.drawable.Drawable.Callback} for the overlay drawable,
* so keep in mind that any existing one set by the application will be lost.
@@ -1220,7 +1242,7 @@
* Sets an offset that should be added to the content source center to obtain
* the position of the magnifier window, when the {@link #show(float, float)}
* method is called. The offset is ignored when {@link #show(float, float, float, float)}
- * is used. The offset can be negative, and it defaults to (0dp, -42dp).
+ * is used. The offset can be negative. It defaults to (0dp, 0dp).
* @param horizontalOffset the horizontal component of the offset
* @param verticalOffset the vertical component of the offset
*/
@@ -1406,8 +1428,8 @@
final Resources resources = Resources.getSystem();
final float density = resources.getDisplayMetrics().density;
final PointF size = new PointF();
- size.x = resources.getDimension(R.dimen.magnifier_width) / density;
- size.y = resources.getDimension(R.dimen.magnifier_height) / density;
+ size.x = resources.getDimension(R.dimen.default_magnifier_width) / density;
+ size.y = resources.getDimension(R.dimen.default_magnifier_height) / density;
return size;
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 74051e2..506d615 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -204,7 +204,7 @@
private View mBaselineView = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mGravity = Gravity.START | Gravity.TOP;
private final Rect mContentBounds = new Rect();
private final Rect mSelfBounds = new Rect();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index c0979fe..7b39efe 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -161,6 +161,7 @@
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;
/**
* Application that hosts the remote views.
@@ -274,6 +275,15 @@
}
/**
+ * 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.
*
@@ -2122,6 +2132,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.
@@ -2326,6 +2373,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");
}
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6cb0eaa..4caf288 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -31,6 +31,7 @@
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
+import android.text.util.Linkify;
import android.util.Log;
import android.view.ActionMode;
import android.view.textclassifier.SelectionEvent;
@@ -687,17 +688,6 @@
mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
}
- @TextClassifier.WidgetType
- private static String getWidetType(TextView textView) {
- if (textView.isTextEditable()) {
- return TextClassifier.WIDGET_TYPE_EDITTEXT;
- }
- if (textView.isTextSelectable()) {
- return TextClassifier.WIDGET_TYPE_TEXTVIEW;
- }
- return TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW;
- }
-
public void logSelectionStarted(
TextClassifier classificationSession,
CharSequence text, int index,
@@ -1045,7 +1035,12 @@
trimText();
final TextClassification classification;
- if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
+ if (Linkify.containsUnsupportedCharacters(mText)) {
+ // Do not show smart actions for text containing unsupported characters.
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ classification = TextClassification.EMPTY;
+ } else if (mContext.getApplicationInfo().targetSdkVersion
+ >= Build.VERSION_CODES.P) {
final TextClassification.Request request =
new TextClassification.Request.Builder(
mTrimmedText, mRelativeStart, mRelativeEnd)
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index d55c09f..79dc670 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1419,27 +1419,10 @@
return Switch.class.getName();
}
+ /** @hide */
@Override
- public void onProvideStructure(ViewStructure structure) {
- super.onProvideStructure(structure);
- onProvideStructureForAssistOrAutofillOrViewCapture(structure);
- }
-
- @Override
- public void onProvideAutofillStructure(ViewStructure structure, int flags) {
- super.onProvideAutofillStructure(structure, flags);
- onProvideStructureForAssistOrAutofillOrViewCapture(structure);
- }
-
- @Override
- public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
- final boolean notifyManager = super.onProvideContentCaptureStructure(structure, flags);
- onProvideStructureForAssistOrAutofillOrViewCapture(structure);
- return notifyManager;
- }
-
- // NOTE: currently there is no difference for any type, so it doesn't take flags
- private void onProvideStructureForAssistOrAutofillOrViewCapture(ViewStructure structure) {
+ protected void onProvideStructure(@NonNull ViewStructure structure,
+ @ViewStructureType int viewFor, int flags) {
CharSequence switchText = isChecked() ? mTextOn : mTextOff;
if (!TextUtils.isEmpty(switchText)) {
CharSequence oldText = structure.getText();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 35be766..85d851a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10942,6 +10942,9 @@
if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
|| viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
if (mLayout == null) {
+ if (viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
+ Log.w(LOG_TAG, "onProvideContentCaptureStructure(): calling assumeLayout()");
+ }
assumeLayout();
}
Layout layout = mLayout;
@@ -12265,13 +12268,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/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index e2b8f7d..5d08a25 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -309,9 +309,6 @@
} else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
prefer = RECOMMEND_INSTALL_INTERNAL;
checkBoth = false;
- } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- prefer = RECOMMEND_INSTALL_EXTERNAL;
- checkBoth = false;
} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
prefer = RECOMMEND_INSTALL_INTERNAL;
checkBoth = false;
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index 387857f..c5bc45a 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -22,6 +22,8 @@
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
+import java.util.List;
+
/**
* Creates class loaders.
*
@@ -37,6 +39,13 @@
DelegateLastClassLoader.class.getName();
/**
+ * Returns the name of the class for PathClassLoader.
+ */
+ public static String getPathClassLoaderName() {
+ return PATH_CLASS_LOADER_NAME;
+ }
+
+ /**
* Returns true if {@code name} is a supported classloader. {@code name} must be a
* binary name of a class, as defined by {@code Class.getName}.
*/
@@ -68,25 +77,43 @@
* is created.
*/
public static ClassLoader createClassLoader(String dexPath,
- String librarySearchPath, ClassLoader parent, String classloaderName) {
+ String librarySearchPath, ClassLoader parent, String classloaderName,
+ List<ClassLoader> sharedLibraries) {
+ ClassLoader[] arrayOfSharedLibraries = (sharedLibraries == null)
+ ? null
+ : sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]);
if (isPathClassLoaderName(classloaderName)) {
- return new PathClassLoader(dexPath, librarySearchPath, parent);
+ return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries);
} else if (isDelegateLastClassLoaderName(classloaderName)) {
- return new DelegateLastClassLoader(dexPath, librarySearchPath, parent);
+ return new DelegateLastClassLoader(dexPath, librarySearchPath, parent,
+ arrayOfSharedLibraries);
}
throw new AssertionError("Invalid classLoaderName: " + classloaderName);
}
/**
+ * Same as {@code createClassLoader} below, but passes a null list of shared
+ * libraries.
+ */
+ public static ClassLoader createClassLoader(String dexPath,
+ String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
+ int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) {
+ return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath,
+ parent, targetSdkVersion, isNamespaceShared, classLoaderName, null);
+ }
+
+
+ /**
* Create a ClassLoader and initialize a linker-namespace for it.
*/
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
- int targetSdkVersion, boolean isNamespaceShared, String classloaderName) {
+ int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
+ List<ClassLoader> sharedLibraries) {
final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
- classloaderName);
+ classLoaderName, sharedLibraries);
boolean isForVendor = false;
for (String path : dexPath.split(":")) {
diff --git a/core/java/com/android/internal/os/KernelCpuProcStringReader.java b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
index 22435ae..b3aec0c 100644
--- a/core/java/com/android/internal/os/KernelCpuProcStringReader.java
+++ b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
@@ -25,6 +25,7 @@
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
@@ -59,6 +60,7 @@
private static final String PROC_UID_FREQ_TIME = "/proc/uid_time_in_state";
private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_concurrent_active_time";
private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_concurrent_policy_time";
+ private static final String PROC_UID_USER_SYS_TIME = "/proc/uid_cputime/show_uid_stat";
private static final KernelCpuProcStringReader FREQ_TIME_READER =
new KernelCpuProcStringReader(PROC_UID_FREQ_TIME);
@@ -66,19 +68,25 @@
new KernelCpuProcStringReader(PROC_UID_ACTIVE_TIME);
private static final KernelCpuProcStringReader CLUSTER_TIME_READER =
new KernelCpuProcStringReader(PROC_UID_CLUSTER_TIME);
+ private static final KernelCpuProcStringReader USER_SYS_TIME_READER =
+ new KernelCpuProcStringReader(PROC_UID_USER_SYS_TIME);
- public static KernelCpuProcStringReader getFreqTimeReaderInstance() {
+ static KernelCpuProcStringReader getFreqTimeReaderInstance() {
return FREQ_TIME_READER;
}
- public static KernelCpuProcStringReader getActiveTimeReaderInstance() {
+ static KernelCpuProcStringReader getActiveTimeReaderInstance() {
return ACTIVE_TIME_READER;
}
- public static KernelCpuProcStringReader getClusterTimeReaderInstance() {
+ static KernelCpuProcStringReader getClusterTimeReaderInstance() {
return CLUSTER_TIME_READER;
}
+ static KernelCpuProcStringReader getUserSysTimeReaderInstance() {
+ return USER_SYS_TIME_READER;
+ }
+
private int mErrors = 0;
private final Path mFile;
private char[] mBuf;
@@ -164,12 +172,12 @@
// ReentrantReadWriteLock allows lock downgrading.
mReadLock.lock();
return new ProcFileIterator(total);
- } catch (FileNotFoundException e) {
+ } catch (FileNotFoundException | NoSuchFileException e) {
mErrors++;
Slog.w(TAG, "File not found. It's normal if not implemented: " + mFile);
} catch (IOException e) {
mErrors++;
- Slog.e(TAG, "Error reading: " + mFile, e);
+ Slog.e(TAG, "Error reading " + mFile, e);
} finally {
StrictMode.setThreadPolicyMask(oldMask);
mWriteLock.unlock();
@@ -193,6 +201,11 @@
mSize = size;
}
+ /** @return Whether there are more lines in the iterator. */
+ public boolean hasNextLine() {
+ return mPos < mSize;
+ }
+
/**
* Fetches the next line. Note that all subsequent return values share the same char[]
* under the hood.
@@ -214,44 +227,6 @@
return CharBuffer.wrap(mBuf, start, i - start);
}
- /**
- * Fetches the next line, converts all numbers into long, and puts into the given long[].
- * To avoid GC, caller should try to use the same array for all calls. All non-numeric
- * chars are treated as delimiters. All numbers are non-negative.
- *
- * @param array An array to store the parsed numbers.
- * @return The number of elements written to the given array. -1 if there is no more line.
- */
- public int nextLineAsArray(long[] array) {
- CharBuffer buf = nextLine();
- if (buf == null) {
- return -1;
- }
- int count = 0;
- long num = -1;
- char c;
-
- while (buf.remaining() > 0 && count < array.length) {
- c = buf.get();
- if (num < 0) {
- if (isNumber(c)) {
- num = c - '0';
- }
- } else {
- if (isNumber(c)) {
- num = num * 10 + c - '0';
- } else {
- array[count++] = num;
- num = -1;
- }
- }
- }
- if (num >= 0) {
- array[count++] = num;
- }
- return count;
- }
-
/** Total size of the proc file in chars. */
public int size() {
return mSize;
@@ -262,8 +237,63 @@
mReadLock.unlock();
}
- private boolean isNumber(char c) {
- return c >= '0' && c <= '9';
+
+ }
+
+ /**
+ * Converts all numbers in the CharBuffer into longs, and puts into the given long[].
+ *
+ * Space and colon are treated as delimiters. All other chars are not allowed. All numbers
+ * are non-negative. To avoid GC, caller should try to use the same array for all calls.
+ *
+ * This method also resets the given buffer to the original position before return so that
+ * it can be read again.
+ *
+ * @param buf The char buffer to be converted.
+ * @param array An array to store the parsed numbers.
+ * @return The number of elements written to the given array. -1 if buf is null, -2 if buf
+ * contains invalid char, -3 if any number overflows.
+ */
+ public static int asLongs(CharBuffer buf, long[] array) {
+ if (buf == null) {
+ return -1;
}
+ final int initialPos = buf.position();
+ int count = 0;
+ long num = -1;
+ char c;
+
+ while (buf.remaining() > 0 && count < array.length) {
+ c = buf.get();
+ if (!(isNumber(c) || c == ' ' || c == ':')) {
+ buf.position(initialPos);
+ return -2;
+ }
+ if (num < 0) {
+ if (isNumber(c)) {
+ num = c - '0';
+ }
+ } else {
+ if (isNumber(c)) {
+ num = num * 10 + c - '0';
+ if (num < 0) {
+ buf.position(initialPos);
+ return -3;
+ }
+ } else {
+ array[count++] = num;
+ num = -1;
+ }
+ }
+ }
+ if (num >= 0) {
+ array[count++] = num;
+ }
+ buf.position(initialPos);
+ return count;
+ }
+
+ private static boolean isNumber(char c) {
+ return c >= '0' && c <= '9';
}
}
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index ade5d05..2e901e4 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -29,6 +29,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.function.Predicate;
/**
* Given a process, will iterate over the child threads of the process, and return the CPU usage
@@ -70,6 +71,11 @@
private static final String THREAD_NAME_FILENAME = "comm";
/**
+ * Glob pattern for the process directory names under {@code proc}
+ */
+ private static final String PROCESS_DIRECTORY_FILTER = "[0-9]*";
+
+ /**
* Default process name when the name can't be read
*/
private static final String DEFAULT_PROCESS_NAME = "unknown_process";
@@ -96,6 +102,24 @@
private static final int NUM_BUCKETS = 8;
/**
+ * Default predicate for what UIDs to check for when getting processes. This filters to only
+ * select UID 1000 (the {@code system} user)
+ */
+ private static final Predicate<Integer> DEFAULT_UID_PREDICATE = uid -> uid == 1000;
+
+ /**
+ * 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;
+
+ /**
+ * Value returned when there was an error getting an integer ID value (e.g. PID, UID)
+ */
+ private static final int ID_ERROR = -1;
+
+ /**
* Where the proc filesystem is mounted
*/
private final Path mProcPath;
@@ -116,8 +140,13 @@
*/
private final FrequencyBucketCreator mFrequencyBucketCreator;
+ private final Injector mInjector;
+
private KernelCpuThreadReader() throws IOException {
- this(DEFAULT_PROC_PATH, DEFAULT_INITIAL_TIME_IN_STATE_PATH);
+ this(
+ DEFAULT_PROC_PATH,
+ DEFAULT_INITIAL_TIME_IN_STATE_PATH,
+ new Injector());
}
/**
@@ -128,9 +157,13 @@
* format
*/
@VisibleForTesting
- public KernelCpuThreadReader(Path procPath, Path initialTimeInStatePath) throws IOException {
+ public KernelCpuThreadReader(
+ Path procPath,
+ Path initialTimeInStatePath,
+ Injector injector) throws IOException {
mProcPath = procPath;
mProcTimeInStateReader = new ProcTimeInStateReader(initialTimeInStatePath);
+ mInjector = injector;
// Copy mProcTimeInState's frequencies and initialize bucketing
final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz();
@@ -154,6 +187,67 @@
}
/**
+ * Get the per-thread CPU usage of all processes belonging to UIDs between {@code [1000, 2000)}
+ */
+ @Nullable
+ public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() {
+ return getProcessCpuUsageByUids(DEFAULT_UID_PREDICATE);
+ }
+
+ /**
+ * Get the per-thread CPU usage of all processes belonging to a set of UIDs
+ *
+ * <p>This function will crawl through all process {@code proc} directories found by the pattern
+ * {@code /proc/[0-9]*}, and then check the UID using {@code /proc/$PID/status}. This takes
+ * approximately 500ms on a Pixel 2. Therefore, this method can be computationally expensive,
+ * and should not be called more than once an hour.
+ *
+ * @param uidPredicate only get usage from processes owned by UIDs that match this predicate
+ */
+ @Nullable
+ public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids(Predicate<Integer> uidPredicate) {
+ if (DEBUG) {
+ Slog.d(TAG, "Reading CPU thread usages for processes owned by UIDs");
+ }
+
+ final ArrayList<ProcessCpuUsage> processCpuUsages = new ArrayList<>();
+
+ try (DirectoryStream<Path> processPaths =
+ Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) {
+ for (Path processPath : processPaths) {
+ final int processId = getProcessId(processPath);
+ final int uid = mInjector.getUidForPid(processId);
+ if (uid == ID_ERROR || processId == ID_ERROR) {
+ continue;
+ }
+ if (!uidPredicate.test(uid)) {
+ continue;
+ }
+
+ final ProcessCpuUsage processCpuUsage =
+ getProcessCpuUsage(processPath, processId, uid);
+ if (processCpuUsage != null) {
+ processCpuUsages.add(processCpuUsage);
+ }
+ }
+ } catch (IOException e) {
+ Slog.w("Failed to iterate over process paths", e);
+ return null;
+ }
+
+ if (processCpuUsages.isEmpty()) {
+ Slog.w(TAG, "Didn't successfully get any process CPU information for UIDs specified");
+ return null;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Read usage for " + processCpuUsages.size() + " processes");
+ }
+
+ return processCpuUsages;
+ }
+
+ /**
* Read all of the CPU usage statistics for each child thread of the current process
*
* @return process CPU usage containing usage of all child threads
@@ -162,8 +256,8 @@
public ProcessCpuUsage getCurrentProcessCpuUsage() {
return getProcessCpuUsage(
mProcPath.resolve("self"),
- Process.myPid(),
- Process.myUid());
+ mInjector.myPid(),
+ mInjector.myUid());
}
/**
@@ -172,7 +266,8 @@
* @param processPath the {@code /proc} path of the thread
* @param processId the ID of the process
* @param uid the ID of the user who owns the process
- * @return process CPU usage containing usage of all child threads
+ * @return process CPU usage containing usage of all child threads. Null if the process exited
+ * and its {@code proc} directory was removed while collecting information
*/
@Nullable
private ProcessCpuUsage getProcessCpuUsage(Path processPath, int processId, int uid) {
@@ -224,7 +319,8 @@
* Get a thread's CPU usage
*
* @param threadDirectory the {@code /proc} directory of the thread
- * @return null in the case that the directory read failed
+ * @return thread CPU usage. Null if the thread exited and its {@code proc} directory was
+ * removed while collecting information
*/
@Nullable
private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) {
@@ -249,6 +345,15 @@
}
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);
}
@@ -280,6 +385,22 @@
}
/**
+ * Get the ID of a process from its path
+ *
+ * @param processPath {@code proc} path of the process
+ * @return the ID, {@link #ID_ERROR} if the path could not be parsed
+ */
+ private int getProcessId(Path processPath) {
+ String fileName = processPath.getFileName().toString();
+ try {
+ return Integer.parseInt(fileName);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Failed to parse " + fileName + " as process ID", e);
+ return ID_ERROR;
+ }
+ }
+
+ /**
* Puts frequencies and usage times into buckets
*/
@VisibleForTesting
@@ -443,4 +564,31 @@
this.usageTimesMillis = usageTimesMillis;
}
}
+
+ /**
+ * Used to inject static methods from {@link Process}
+ */
+ @VisibleForTesting
+ public static class Injector {
+ /**
+ * Get the PID of the current process
+ */
+ public int myPid() {
+ return Process.myPid();
+ }
+
+ /**
+ * Get the UID that owns the current process
+ */
+ public int myUid() {
+ return Process.myUid();
+ }
+
+ /**
+ * Get the UID for the process with ID {@code pid}
+ */
+ public int getUidForPid(int pid) {
+ return Process.getUidForPid(pid);
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
new file mode 100644
index 0000000..7021b57
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -0,0 +1,776 @@
+/*
+ * 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.os;
+
+import static com.android.internal.os.KernelCpuProcStringReader.asLongs;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
+
+import java.io.BufferedReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Reads per-UID CPU time proc files. Concrete implementations are all nested inside.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} or {@link #readAbsolute} call
+ * within {@link #mMinTimeBetweenRead}. The throttler can be enable / disabled via a param in
+ * the constructor.
+ *
+ * This class and its subclasses are NOT thread-safe and NOT designed to be accessed by more than
+ * one caller since each caller has its own view of delta.
+ *
+ * @param <T> The type of CPU time for the callback.
+ */
+public abstract class KernelCpuUidTimeReader<T> {
+ protected static final boolean DEBUG = false;
+ private static final long DEFAULT_MIN_TIME_BETWEEN_READ = 1000L; // In milliseconds
+
+ final String mTag = this.getClass().getSimpleName();
+ final SparseArray<T> mLastTimes = new SparseArray<>();
+ final KernelCpuProcStringReader mReader;
+ final boolean mThrottle;
+ private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
+ private long mLastReadTimeMs = 0;
+
+ /**
+ * Callback interface for processing each line of the proc file.
+ *
+ * @param <T> The type of CPU time for the callback function.
+ */
+ public interface Callback<T> {
+ /**
+ * @param uid UID of the app
+ * @param time Time spent. The exact data structure depends on subclass implementation.
+ */
+ void onUidCpuTime(int uid, T time);
+ }
+
+ KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ mReader = reader;
+ mThrottle = throttle;
+ }
+
+ /**
+ * Reads the proc file, calling into the callback with a delta of time for each UID.
+ *
+ * @param cb The callback to invoke for each line of the proc file. If null,the data is
+ * consumed and subsequent calls to readDelta will provide a fresh delta.
+ */
+ public void readDelta(@Nullable Callback<T> cb) {
+ if (!mThrottle) {
+ readDeltaImpl(cb);
+ return;
+ }
+ final long currTimeMs = SystemClock.elapsedRealtime();
+ if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+ if (DEBUG) {
+ Slog.d(mTag, "Throttle readDelta");
+ }
+ return;
+ }
+ readDeltaImpl(cb);
+ mLastReadTimeMs = currTimeMs;
+ }
+
+ /**
+ * Reads the proc file, calling into the callback with cumulative time for each UID.
+ *
+ * @param cb The callback to invoke for each line of the proc file. It cannot be null.
+ */
+ public void readAbsolute(Callback<T> cb) {
+ if (!mThrottle) {
+ readAbsoluteImpl(cb);
+ return;
+ }
+ final long currTimeMs = SystemClock.elapsedRealtime();
+ if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+ if (DEBUG) {
+ Slog.d(mTag, "Throttle readAbsolute");
+ }
+ return;
+ }
+ readAbsoluteImpl(cb);
+ mLastReadTimeMs = currTimeMs;
+ }
+
+ abstract void readDeltaImpl(@Nullable Callback<T> cb);
+
+ abstract void readAbsoluteImpl(Callback<T> callback);
+
+ /**
+ * Removes the UID from internal accounting data. This method, overridden in
+ * {@link KernelCpuUidUserSysTimeReader}, also removes the UID from the kernel module.
+ *
+ * @param uid The UID to remove.
+ * @see KernelCpuUidUserSysTimeReader#removeUid(int)
+ */
+ public void removeUid(int uid) {
+ mLastTimes.delete(uid);
+ }
+
+ /**
+ * Removes UIDs in a given range from internal accounting data. This method, overridden in
+ * {@link KernelCpuUidUserSysTimeReader}, also removes the UIDs from the kernel module.
+ *
+ * @param startUid the first uid to remove.
+ * @param endUid the last uid to remove.
+ * @see KernelCpuUidUserSysTimeReader#removeUidsInRange(int, int)
+ */
+ public void removeUidsInRange(int startUid, int endUid) {
+ if (endUid < startUid) {
+ Slog.e(mTag, "start UID " + startUid + " > end UID " + endUid);
+ return;
+ }
+ mLastTimes.put(startUid, null);
+ mLastTimes.put(endUid, null);
+ final int firstIndex = mLastTimes.indexOfKey(startUid);
+ final int lastIndex = mLastTimes.indexOfKey(endUid);
+ mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ }
+
+ /**
+ * Set the minimum time in milliseconds between reads. If throttle is not enabled, this method
+ * has no effect.
+ *
+ * @param minTimeBetweenRead The minimum time in milliseconds.
+ */
+ public void setThrottle(long minTimeBetweenRead) {
+ if (mThrottle && minTimeBetweenRead >= 0) {
+ mMinTimeBetweenRead = minTimeBetweenRead;
+ }
+ }
+
+ /**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ */
+ public static class KernelCpuUidUserSysTimeReader extends KernelCpuUidTimeReader<long[]> {
+ private static final String REMOVE_UID_PROC_FILE = "/proc/uid_cputime/remove_uid_range";
+
+ // [uid, user_time, system_time, (maybe) power_in_milli-amp-micro_seconds]
+ private final long[] mBuffer = new long[4];
+ // A reusable array to hold [user_time, system_time] for the callback.
+ private final long[] mUsrSysTime = new long[2];
+
+ public KernelCpuUidUserSysTimeReader(boolean throttle) {
+ super(KernelCpuProcStringReader.getUserSysTimeReaderInstance(), throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidUserSysTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ super(reader, throttle);
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (iter == null) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) < 3) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ final int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[2];
+ mLastTimes.put(uid, lastTimes);
+ }
+ final long currUsrTimeUs = mBuffer[1];
+ final long currSysTimeUs = mBuffer[2];
+ mUsrSysTime[0] = currUsrTimeUs - lastTimes[0];
+ mUsrSysTime[1] = currSysTimeUs - lastTimes[1];
+
+ if (mUsrSysTime[0] < 0 || mUsrSysTime[1] < 0) {
+ Slog.e(mTag, "Negative user/sys time delta for UID=" + uid
+ + "\nPrev times: u=" + lastTimes[0] + " s=" + lastTimes[1]
+ + " Curr times: u=" + currUsrTimeUs + " s=" + currSysTimeUs);
+ } else if (mUsrSysTime[0] > 0 || mUsrSysTime[1] > 0) {
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mUsrSysTime);
+ }
+ }
+ lastTimes[0] = currUsrTimeUs;
+ lastTimes[1] = currSysTimeUs;
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (iter == null) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) < 3) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ mUsrSysTime[0] = mBuffer[1]; // User time in microseconds
+ mUsrSysTime[1] = mBuffer[2]; // System time in microseconds
+ cb.onUidCpuTime((int) mBuffer[0], mUsrSysTime);
+ }
+ }
+ }
+
+ @Override
+ public void removeUid(int uid) {
+ super.removeUid(uid);
+ removeUidsFromKernelModule(uid, uid);
+ }
+
+ @Override
+ public void removeUidsInRange(int startUid, int endUid) {
+ super.removeUidsInRange(startUid, endUid);
+ removeUidsFromKernelModule(startUid, endUid);
+ }
+
+ /**
+ * Removes UIDs in a given range from the kernel module and internal accounting data. Only
+ * {@link BatteryStatsImpl} and its child processes should call this, as the change on
+ * Kernel is
+ * visible system wide.
+ *
+ * @param startUid the first uid to remove
+ * @param endUid the last uid to remove
+ */
+ private void removeUidsFromKernelModule(int startUid, int endUid) {
+ Slog.d(mTag, "Removing uids " + startUid + "-" + endUid);
+ final int oldMask = StrictMode.allowThreadDiskWritesMask();
+ try (FileWriter writer = new FileWriter(REMOVE_UID_PROC_FILE)) {
+ writer.write(startUid + "-" + endUid);
+ writer.flush();
+ } catch (IOException e) {
+ Slog.e(mTag, "failed to remove uids " + startUid + " - " + endUid
+ + " from uid_cputime module", e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ }
+ }
+
+ /**
+ * Reads /proc/uid_time_in_state which has the format:
+ *
+ * uid: [freq1] [freq2] [freq3] ...
+ * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
+ * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
+ * ...
+ *
+ * This provides the times a UID's processes spent executing at each different cpu frequency.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ */
+ public static class KernelCpuUidFreqTimeReader extends KernelCpuUidTimeReader<long[]> {
+ private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
+ // We check the existence of proc file a few times (just in case it is not ready yet when we
+ // start reading) and if it is not available, we simply ignore further read requests.
+ private static final int MAX_ERROR_COUNT = 5;
+
+ private final Path mProcFilePath;
+ private long[] mBuffer;
+ private long[] mCurTimes;
+ private long[] mDeltaTimes;
+ private long[] mCpuFreqs;
+
+ private int mFreqCount = 0;
+ private int mErrors = 0;
+ private boolean mPerClusterTimesAvailable;
+ private boolean mAllUidTimesAvailable = true;
+
+ public KernelCpuUidFreqTimeReader(boolean throttle) {
+ this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
+ throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
+ boolean throttle) {
+ super(reader, throttle);
+ mProcFilePath = Paths.get(procFile);
+ }
+
+ /**
+ * @return Whether per-cluster times are available.
+ */
+ public boolean perClusterTimesAvailable() {
+ return mPerClusterTimesAvailable;
+ }
+
+ /**
+ * @return Whether all-UID times are available.
+ */
+ public boolean allUidTimesAvailable() {
+ return mAllUidTimesAvailable;
+ }
+
+ /**
+ * @return A map of all UIDs to their CPU time-in-state array in milliseconds.
+ */
+ public SparseArray<long[]> getAllUidCpuFreqTimeMs() {
+ return mLastTimes;
+ }
+
+ /**
+ * Reads a list of CPU frequencies from /proc/uid_time_in_state. Uses a given PowerProfile
+ * to determine if per-cluster times are available.
+ *
+ * @param powerProfile The PowerProfile to compare against.
+ * @return A long[] of CPU frequencies in Hz.
+ */
+ public long[] readFreqs(@NonNull PowerProfile powerProfile) {
+ checkNotNull(powerProfile);
+ if (mCpuFreqs != null) {
+ // No need to read cpu freqs more than once.
+ return mCpuFreqs;
+ }
+ if (!mAllUidTimesAvailable) {
+ return null;
+ }
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
+ if (readFreqs(reader.readLine()) == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ if (++mErrors >= MAX_ERROR_COUNT) {
+ mAllUidTimesAvailable = false;
+ }
+ Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+ return null;
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ // Check if the freqs in the proc file correspond to per-cluster freqs.
+ final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
+ final int numClusters = powerProfile.getNumCpuClusters();
+ if (numClusterFreqs.size() == numClusters) {
+ mPerClusterTimesAvailable = true;
+ for (int i = 0; i < numClusters; ++i) {
+ if (numClusterFreqs.get(i) != powerProfile.getNumSpeedStepsInCpuCluster(i)) {
+ mPerClusterTimesAvailable = false;
+ break;
+ }
+ }
+ } else {
+ mPerClusterTimesAvailable = false;
+ }
+ Slog.i(mTag, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
+ return mCpuFreqs;
+ }
+
+ private long[] readFreqs(String line) {
+ if (line == null) {
+ return null;
+ }
+ final String[] lineArray = line.split(" ");
+ if (lineArray.length <= 1) {
+ Slog.wtf(mTag, "Malformed freq line: " + line);
+ return null;
+ }
+ mFreqCount = lineArray.length - 1;
+ mCpuFreqs = new long[mFreqCount];
+ mCurTimes = new long[mFreqCount];
+ mDeltaTimes = new long[mFreqCount];
+ mBuffer = new long[mFreqCount + 1];
+ for (int i = 0; i < mFreqCount; ++i) {
+ mCpuFreqs[i] = Long.parseLong(lineArray[i + 1], 10);
+ }
+ return mCpuFreqs;
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ final int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mFreqCount];
+ mLastTimes.put(uid, lastTimes);
+ }
+ copyToCurTimes();
+ boolean notify = false;
+ boolean valid = true;
+ for (int i = 0; i < mFreqCount; i++) {
+ // Unit is 10ms.
+ mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+ if (mDeltaTimes[i] < 0) {
+ Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+ valid = false;
+ }
+ notify |= mDeltaTimes[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mDeltaTimes);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ copyToCurTimes();
+ cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
+ }
+ }
+ }
+
+ private void copyToCurTimes() {
+ for (int i = 0; i < mFreqCount; i++) {
+ mCurTimes[i] = mBuffer[i + 1] * 10;
+ }
+ }
+
+ private boolean checkPrecondition(ProcFileIterator iter) {
+ if (iter == null || !iter.hasNextLine()) {
+ // Error logged in KernelCpuProcStringReader.
+ return false;
+ }
+ CharBuffer line = iter.nextLine();
+ if (mCpuFreqs != null) {
+ return true;
+ }
+ return readFreqs(line.toString()) != null;
+ }
+
+ /**
+ * Extracts no. of cpu clusters and no. of freqs in each of these clusters from the freqs
+ * read from the proc file.
+ *
+ * We need to assume that freqs in each cluster are strictly increasing.
+ * For e.g. if the freqs read from proc file are: 12, 34, 15, 45, 12, 15, 52. Then it means
+ * there are 3 clusters: (12, 34), (15, 45), (12, 15, 52)
+ *
+ * @return an IntArray filled with no. of freqs in each cluster.
+ */
+ private IntArray extractClusterInfoFromProcFileFreqs() {
+ final IntArray numClusterFreqs = new IntArray();
+ int freqsFound = 0;
+ for (int i = 0; i < mFreqCount; ++i) {
+ freqsFound++;
+ if (i + 1 == mFreqCount || mCpuFreqs[i + 1] <= mCpuFreqs[i]) {
+ numClusterFreqs.add(freqsFound);
+ freqsFound = 0;
+ }
+ }
+ return numClusterFreqs;
+ }
+ }
+
+ /**
+ * Reads /proc/uid_concurrent_active_time and reports CPU active time to BatteryStats to
+ * compute {@link PowerProfile#POWER_CPU_ACTIVE}.
+ *
+ * /proc/uid_concurrent_active_time has the following format:
+ * cpus: n
+ * uid0: time0a, time0b, ..., time0n,
+ * uid1: time1a, time1b, ..., time1n,
+ * uid2: time2a, time2b, ..., time2n,
+ * ...
+ * where n is the total number of cpus (num_possible_cpus)
+ * timeXn means the CPU time that a UID X spent running concurrently with n other processes.
+ *
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ */
+ public static class KernelCpuUidActiveTimeReader extends KernelCpuUidTimeReader<Long> {
+ private int mCores = 0;
+ private long[] mBuffer;
+
+ public KernelCpuUidActiveTimeReader(boolean throttle) {
+ super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ super(reader, throttle);
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<Long> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ int uid = (int) mBuffer[0];
+ long cpuActiveTime = sumActiveTime(mBuffer);
+ if (cpuActiveTime > 0) {
+ long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
+ if (delta > 0) {
+ mLastTimes.put(uid, cpuActiveTime);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, delta);
+ }
+ } else if (delta < 0) {
+ Slog.e(mTag, "Negative delta from active time proc: " + delta);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<Long> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ long cpuActiveTime = sumActiveTime(mBuffer);
+ if (cpuActiveTime > 0) {
+ cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
+ }
+ }
+ }
+ }
+
+ private static long sumActiveTime(long[] times) {
+ // UID is stored at times[0].
+ double sum = 0;
+ for (int i = 1; i < times.length; i++) {
+ sum += (double) times[i] * 10 / i; // Unit is 10ms.
+ }
+ return (long) sum;
+ }
+
+ private boolean checkPrecondition(ProcFileIterator iter) {
+ if (iter == null || !iter.hasNextLine()) {
+ // Error logged in KernelCpuProcStringReader.
+ return false;
+ }
+ CharBuffer line = iter.nextLine();
+ if (mCores > 0) {
+ return true;
+ }
+
+ String str = line.toString();
+ if (!str.startsWith("cpus:")) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + line);
+ return false;
+ }
+ int cores = Integer.parseInt(str.substring(5).trim(), 10);
+ if (cores <= 0) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + line);
+ return false;
+ }
+ mCores = cores;
+ mBuffer = new long[mCores + 1]; // UID is stored at mBuffer[0].
+ return true;
+ }
+ }
+
+
+ /**
+ * Reads /proc/uid_concurrent_policy_time and reports CPU cluster times to BatteryStats to
+ * compute cluster power. See {@link PowerProfile#getAveragePowerForCpuCluster(int)}.
+ *
+ * /proc/uid_concurrent_policy_time has the following format:
+ * policyX: x policyY: y policyZ: z...
+ * uid1, time1a, time1b, ..., time1n,
+ * uid2, time2a, time2b, ..., time2n,
+ * ...
+ * The first line lists all policies (i.e. clusters) followed by # cores in each policy.
+ * Each uid is followed by x time entries corresponding to the time it spent on clusterX
+ * running concurrently with 0, 1, 2, ..., x - 1 other processes, then followed by y, z, ...
+ * time entries.
+ *
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ */
+ public static class KernelCpuUidClusterTimeReader extends KernelCpuUidTimeReader<long[]> {
+ private int mNumClusters;
+ private int mNumCores;
+ private int[] mCoresOnClusters; // # cores on each cluster.
+ private long[] mBuffer; // To store data returned from ProcFileIterator.
+ private long[] mCurTime;
+ private long[] mDeltaTime;
+
+ public KernelCpuUidClusterTimeReader(boolean throttle) {
+ super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ super(reader, throttle);
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mNumClusters];
+ mLastTimes.put(uid, lastTimes);
+ }
+ sumClusterTime();
+ boolean valid = true;
+ boolean notify = false;
+ for (int i = 0; i < mNumClusters; i++) {
+ mDeltaTime[i] = mCurTime[i] - lastTimes[i];
+ if (mDeltaTime[i] < 0) {
+ Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+ valid = false;
+ }
+ notify |= mDeltaTime[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mDeltaTime);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ sumClusterTime();
+ cb.onUidCpuTime((int) mBuffer[0], mCurTime);
+ }
+ }
+ }
+
+ private void sumClusterTime() {
+ // UID is stored at mBuffer[0].
+ int core = 1;
+ for (int i = 0; i < mNumClusters; i++) {
+ double sum = 0;
+ for (int j = 1; j <= mCoresOnClusters[i]; j++) {
+ sum += (double) mBuffer[core++] * 10 / j; // Unit is 10ms.
+ }
+ mCurTime[i] = (long) sum;
+ }
+ }
+
+ private boolean checkPrecondition(ProcFileIterator iter) {
+ if (iter == null || !iter.hasNextLine()) {
+ // Error logged in KernelCpuProcStringReader.
+ return false;
+ }
+ CharBuffer line = iter.nextLine();
+ if (mNumClusters > 0) {
+ return true;
+ }
+ // Parse # cores in clusters.
+ String[] lineArray = line.toString().split(" ");
+ if (lineArray.length % 2 != 0) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + line);
+ return false;
+ }
+ int[] clusters = new int[lineArray.length / 2];
+ int cores = 0;
+ for (int i = 0; i < clusters.length; i++) {
+ if (!lineArray[i * 2].startsWith("policy")) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + line);
+ return false;
+ }
+ clusters[i] = Integer.parseInt(lineArray[i * 2 + 1], 10);
+ cores += clusters[i];
+ }
+ mNumClusters = clusters.length;
+ mNumCores = cores;
+ mCoresOnClusters = clusters;
+ mBuffer = new long[cores + 1];
+ mCurTime = new long[mNumClusters];
+ mDeltaTime = new long[mNumClusters];
+ return true;
+ }
+ }
+
+}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 2b661c2..cf2a297 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -50,6 +50,7 @@
private int mSamplingInterval;
private CachedDeviceState.Readonly mDeviceState;
private long mStartTime = System.currentTimeMillis();
+ private boolean mAddDebugEntries = false;
public LooperStats(int samplingInterval, int entriesSizeCap) {
this.mSamplingInterval = samplingInterval;
@@ -60,6 +61,10 @@
mDeviceState = deviceState;
}
+ public void setAddDebugEntries(boolean addDebugEntries) {
+ mAddDebugEntries = addDebugEntries;
+ }
+
@Override
public Object messageDispatchStarting() {
if (deviceStateAllowsCollection() && shouldCollectDetailedData()) {
@@ -142,9 +147,22 @@
// Add the overflow and collision entries only if they have any data.
maybeAddSpecialEntry(exportedEntries, mOverflowEntry);
maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
+ // Debug entries added to help validate the data.
+ if (mAddDebugEntries) {
+ exportedEntries.add(createDebugEntry("start_time_millis", mStartTime));
+ exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+ }
return exportedEntries;
}
+ private ExportedEntry createDebugEntry(String variableName, long value) {
+ final Entry entry = new Entry("__DEBUG_" + variableName);
+ entry.messageCount = 1;
+ entry.recordedMessageCount = 1;
+ entry.maxDelayMillis = value;
+ return new ExportedEntry(entry);
+ }
+
/** Returns a timestamp indicating when the statistics were last reset. */
public long getStartTimeMillis() {
return mStartTime;
diff --git a/core/java/com/android/internal/os/ProcTimeInStateReader.java b/core/java/com/android/internal/os/ProcTimeInStateReader.java
index 3a63498..2318473 100644
--- a/core/java/com/android/internal/os/ProcTimeInStateReader.java
+++ b/core/java/com/android/internal/os/ProcTimeInStateReader.java
@@ -19,9 +19,15 @@
import android.annotation.Nullable;
import android.os.Process;
+import com.android.internal.util.ArrayUtils;
+
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
/**
* Reads and parses {@code time_in_state} files in the {@code proc} filesystem.
@@ -43,6 +49,17 @@
*
* This file would indicate that the CPU has spent 30 milliseconds at frequency 300,000KHz (300Mhz)
* and 10 milliseconds at frequency 1,900,800KHz (1.9GHz).
+ *
+ * <p>This class will also read {@code time_in_state} files with headers, such as:
+ * <pre>
+ * cpu0
+ * 300000 3
+ * 364800 0
+ * ...
+ * cpu4
+ * 300000 1
+ * 364800 4
+ * </pre>
*/
public class ProcTimeInStateReader {
private static final String TAG = "ProcTimeInStateReader";
@@ -51,24 +68,28 @@
* The format of a single line of the {@code time_in_state} file that exports the frequency
* values
*/
- private static final int[] TIME_IN_STATE_LINE_FREQUENCY_FORMAT = {
+ private static final List<Integer> TIME_IN_STATE_LINE_FREQUENCY_FORMAT = Arrays.asList(
Process.PROC_OUT_LONG | Process.PROC_SPACE_TERM,
- Process.PROC_NEWLINE_TERM,
- };
+ Process.PROC_NEWLINE_TERM
+ );
/**
* The format of a single line of the {@code time_in_state} file that exports the time values
*/
- private static final int[] TIME_IN_STATE_LINE_TIME_FORMAT = {
+ private static final List<Integer> TIME_IN_STATE_LINE_TIME_FORMAT = Arrays.asList(
Process.PROC_SPACE_TERM,
- Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM,
- };
+ Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM
+ );
/**
- * The format of the {@code time_in_state} file, defined using {@link Process}'s {@code
- * PROC_OUT_LONG} and related variables
- *
- * Defined on first successful read of {@code time_in_state} file.
+ * The format of a header line of the {@code time_in_state} file
+ */
+ private static final List<Integer> TIME_IN_STATE_HEADER_LINE_FORMAT =
+ Collections.singletonList(Process.PROC_NEWLINE_TERM);
+
+ /**
+ * The format of the {@code time_in_state} file to extract times, defined using {@link
+ * Process}'s {@code PROC_OUT_LONG} and related variables
*/
private int[] mTimeInStateTimeFormat;
@@ -141,46 +162,44 @@
// Read the bytes of the `time_in_state` file
byte[] timeInStateBytes = Files.readAllBytes(timeInStatePath);
- // The number of lines in the `time_in_state` file is the number of frequencies available
+ // Iterate over the lines of the time_in_state file, for each one adding a line to the
+ // formats. These formats are used to extract either the frequencies or the times from a
+ // time_in_state file
+ // Also check if each line is a header, and handle this in the created format arrays
+ ArrayList<Integer> timeInStateFrequencyFormat = new ArrayList<>();
+ ArrayList<Integer> timeInStateTimeFormat = new ArrayList<>();
int numFrequencies = 0;
for (int i = 0; i < timeInStateBytes.length; i++) {
- if (timeInStateBytes[i] == '\n') {
+ // If the first character of the line is not a digit, we treat it as a header
+ if (!Character.isDigit(timeInStateBytes[i])) {
+ timeInStateFrequencyFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT);
+ timeInStateTimeFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT);
+ } else {
+ timeInStateFrequencyFormat.addAll(TIME_IN_STATE_LINE_FREQUENCY_FORMAT);
+ timeInStateTimeFormat.addAll(TIME_IN_STATE_LINE_TIME_FORMAT);
numFrequencies++;
}
- }
- if (numFrequencies == 0) {
- throw new IOException("Empty time_in_state file");
+ // Go to the next line
+ while (i < timeInStateBytes.length && timeInStateBytes[i] != '\n') {
+ i++;
+ }
}
- // Set `mTimeInStateTimeFormat` and `timeInStateFrequencyFormat` to the correct length, and
- // then copy in the `TIME_IN_STATE_{FREQUENCY,TIME}_LINE_FORMAT` until it's full. As we only
- // use the frequency format in this method, it is not an member variable.
- final int[] timeInStateTimeFormat =
- new int[numFrequencies * TIME_IN_STATE_LINE_TIME_FORMAT.length];
- final int[] timeInStateFrequencyFormat =
- new int[numFrequencies * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length];
- for (int i = 0; i < numFrequencies; i++) {
- System.arraycopy(
- TIME_IN_STATE_LINE_FREQUENCY_FORMAT, 0, timeInStateFrequencyFormat,
- i * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length,
- TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length);
- System.arraycopy(
- TIME_IN_STATE_LINE_TIME_FORMAT, 0, timeInStateTimeFormat,
- i * TIME_IN_STATE_LINE_TIME_FORMAT.length,
- TIME_IN_STATE_LINE_TIME_FORMAT.length);
+ if (numFrequencies == 0) {
+ throw new IOException("Empty time_in_state file");
}
// Read the frequencies from the `time_in_state` file and store them, as they will be the
// same for every `time_in_state` file
final long[] readLongs = new long[numFrequencies];
final boolean readSuccess = Process.parseProcLine(
- timeInStateBytes, 0, timeInStateBytes.length, timeInStateFrequencyFormat,
- null, readLongs, null);
+ timeInStateBytes, 0, timeInStateBytes.length,
+ ArrayUtils.convertToIntArray(timeInStateFrequencyFormat), null, readLongs, null);
if (!readSuccess) {
throw new IOException("Failed to parse time_in_state file");
}
- mTimeInStateTimeFormat = timeInStateTimeFormat;
+ mTimeInStateTimeFormat = ArrayUtils.convertToIntArray(timeInStateTimeFormat);
mFrequenciesKhz = readLongs;
}
}
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/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 9a7094a..b7ffb57 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -16,6 +16,7 @@
package com.android.internal.statusbar;
+import android.app.Notification;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Bundle;
@@ -55,7 +56,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);
@@ -66,7 +67,7 @@
void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
void onNotificationDirectReplied(String key);
void onNotificationSmartRepliesAdded(in String key, in int replyCount);
- void onNotificationSmartReplySent(in String key, in int replyIndex);
+ 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);
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/media/java/android/media/ISessionTokensListener.aidl b/core/java/com/android/internal/util/function/NonaConsumer.java
similarity index 66%
copy from media/java/android/media/ISessionTokensListener.aidl
copy to core/java/com/android/internal/util/function/NonaConsumer.java
index c83a19e..3e7ce2b 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/com/android/internal/util/function/NonaConsumer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,14 +14,15 @@
* limitations under the License.
*/
-package android.media;
+package com.android.internal.util.function;
-import android.os.Bundle;
+import java.util.function.Consumer;
/**
- * Listens for changes to the list of session tokens.
+ * A 9-argument {@link Consumer}
+ *
* @hide
*/
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
+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/media/java/android/media/ISessionTokensListener.aidl b/core/java/com/android/internal/util/function/NonaFunction.java
similarity index 66%
copy from media/java/android/media/ISessionTokensListener.aidl
copy to core/java/com/android/internal/util/function/NonaFunction.java
index c83a19e..560b4f1 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/com/android/internal/util/function/NonaFunction.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,14 +14,15 @@
* limitations under the License.
*/
-package android.media;
+package com.android.internal.util.function;
-import android.os.Bundle;
+import java.util.function.Function;
/**
- * Listens for changes to the list of session tokens.
+ * A 9-argument {@link Function}
+ *
* @hide
*/
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
+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/media/java/android/media/ISessionTokensListener.aidl b/core/java/com/android/internal/util/function/NonaPredicate.java
similarity index 65%
copy from media/java/android/media/ISessionTokensListener.aidl
copy to core/java/com/android/internal/util/function/NonaPredicate.java
index c83a19e..c1e6f37 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/com/android/internal/util/function/NonaPredicate.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,14 +14,15 @@
* limitations under the License.
*/
-package android.media;
+package com.android.internal.util.function;
-import android.os.Bundle;
+import java.util.function.Predicate;
/**
- * Listens for changes to the list of session tokens.
+ * A 9-argument {@link Predicate}
+ *
* @hide
*/
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
+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/media/java/android/media/ISessionTokensListener.aidl b/core/java/com/android/internal/util/function/OctConsumer.java
similarity index 66%
copy from media/java/android/media/ISessionTokensListener.aidl
copy to core/java/com/android/internal/util/function/OctConsumer.java
index c83a19e..83ee305 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/com/android/internal/util/function/OctConsumer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,14 +14,15 @@
* limitations under the License.
*/
-package android.media;
+package com.android.internal.util.function;
-import android.os.Bundle;
+import java.util.function.Consumer;
/**
- * Listens for changes to the list of session tokens.
+ * A 8-argument {@link Consumer}
+ *
* @hide
*/
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
+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/media/java/android/media/ISessionTokensListener.aidl b/core/java/com/android/internal/util/function/OctFunction.java
similarity index 66%
copy from media/java/android/media/ISessionTokensListener.aidl
copy to core/java/com/android/internal/util/function/OctFunction.java
index c83a19e..cb16624 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/com/android/internal/util/function/OctFunction.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,14 +14,15 @@
* limitations under the License.
*/
-package android.media;
+package com.android.internal.util.function;
-import android.os.Bundle;
+import java.util.function.Function;
/**
- * Listens for changes to the list of session tokens.
+ * A 8-argument {@link Function}
+ *
* @hide
*/
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
+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/media/java/android/media/ISessionTokensListener.aidl b/core/java/com/android/internal/util/function/OctPredicate.java
similarity index 66%
copy from media/java/android/media/ISessionTokensListener.aidl
copy to core/java/com/android/internal/util/function/OctPredicate.java
index c83a19e..7f36d6ac 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/core/java/com/android/internal/util/function/OctPredicate.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,14 +14,15 @@
* limitations under the License.
*/
-package android.media;
+package com.android.internal.util.function;
-import android.os.Bundle;
+import java.util.function.Predicate;
/**
- * Listens for changes to the list of session tokens.
+ * A 8-argument {@link Predicate}
+ *
* @hide
*/
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
+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..36fe4fc 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -27,6 +27,7 @@
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.PointerIcon;
+import android.view.InsetsState;
import com.android.internal.os.IResultReceiver;
@@ -53,6 +54,10 @@
}
@Override
+ public void insetsChanged(InsetsState insetsState) {
+ }
+
+ @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 15745e9..8495850 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -25,6 +25,7 @@
import android.os.Build;
import android.os.Environment;
import android.os.Process;
+import android.os.SystemProperties;
import android.os.storage.StorageManager;
import android.permission.PermissionManager.SplitPermissionInfo;
import android.text.TextUtils;
@@ -930,6 +931,16 @@
XmlUtils.skipCurrentTag(parser);
}
}
+ // 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 (newPermissions.contains(android.Manifest.permission.READ_MEDIA_AUDIO) ||
+ newPermissions.contains(android.Manifest.permission.READ_MEDIA_VIDEO) ||
+ newPermissions.contains(android.Manifest.permission.READ_MEDIA_IMAGES)) {
+ return;
+ }
+ }
if (!newPermissions.isEmpty()) {
mSplitPermissions.add(new SplitPermissionInfo(splitPerm, newPermissions, targetSdk));
}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index eada690..bc1d5cc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1450,8 +1450,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/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_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_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..04f0a53 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) {
@@ -1351,7 +1347,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},
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index ccbe0ee..49d5007 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -692,14 +692,8 @@
static long get_allocated_vmalloc_memory() {
char line[1024];
- // Ignored tags that don't actually consume memory (ie remappings)
- static const char* const ignored_tags[] = {
- "ioremap",
- "map_lowmem",
- "vm_map_ram",
- NULL
- };
- long size, vmalloc_allocated_size = 0;
+
+ long vmalloc_allocated_size = 0;
UniqueFile fp = MakeUniqueFile("/proc/vmallocinfo", "re");
if (fp == nullptr) {
@@ -710,17 +704,15 @@
if (fgets(line, 1024, fp.get()) == NULL) {
break;
}
- bool valid_line = true;
- int i = 0;
- while (ignored_tags[i]) {
- if (strstr(line, ignored_tags[i]) != NULL) {
- valid_line = false;
- break;
- }
- i++;
+
+ // check to see if there are pages mapped in vmalloc area
+ if (!strstr(line, "pages=")) {
+ continue;
}
- if (valid_line && (sscanf(line, "%*x-%*x %ld", &size) == 1)) {
- vmalloc_allocated_size += size;
+
+ long nr_pages;
+ if (sscanf(line, "%*x-%*x %*ld %*s pages=%ld", &nr_pages) == 1) {
+ vmalloc_allocated_size += (nr_pages * getpagesize());
}
}
return vmalloc_allocated_size;
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/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 7de8020..d917536 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;
@@ -987,5 +991,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 = 145;
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index b0dbaa0..679a1d2 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -97,7 +97,7 @@
message WindowManagerPolicyProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional int32 last_system_ui_flags = 1;
+ optional int32 last_system_ui_flags = 1 [deprecated=true];
enum UserRotationMode {
USER_ROTATION_FREE = 0;
USER_ROTATION_LOCKED = 1;
@@ -108,18 +108,18 @@
optional bool screen_on_fully = 5;
optional bool keyguard_draw_complete = 6;
optional bool window_manager_draw_complete = 7;
- optional string focused_app_token = 8;
- optional IdentifierProto focused_window = 9;
- optional IdentifierProto top_fullscreen_opaque_window = 10;
- optional IdentifierProto top_fullscreen_opaque_or_dimming_window = 11;
+ optional string focused_app_token = 8 [deprecated=true];
+ optional IdentifierProto focused_window = 9 [deprecated=true];
+ optional IdentifierProto top_fullscreen_opaque_window = 10 [deprecated=true];
+ optional IdentifierProto top_fullscreen_opaque_or_dimming_window = 11 [deprecated=true];
optional bool keyguard_occluded = 12;
optional bool keyguard_occluded_changed = 13;
optional bool keyguard_occluded_pending = 14;
- optional bool force_status_bar = 15;
- optional bool force_status_bar_from_keyguard = 16;
- optional BarControllerProto status_bar = 17;
- optional BarControllerProto navigation_bar = 18;
- optional WindowOrientationListenerProto orientation_listener = 19;
+ optional bool force_status_bar = 15 [deprecated=true];
+ optional bool force_status_bar_from_keyguard = 16 [deprecated=true];
+ optional BarControllerProto status_bar = 17 [deprecated=true];
+ optional BarControllerProto navigation_bar = 18 [deprecated=true];
+ optional WindowOrientationListenerProto orientation_listener = 19 [deprecated=true];
optional KeyguardServiceDelegateProto keyguard_delegate = 20;
}
@@ -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;
}
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/media/java/android/media/update/ProviderCreator.java b/core/proto/android/stats/devicepolicy/device_policy.proto
similarity index 73%
copy from media/java/android/media/update/ProviderCreator.java
copy to core/proto/android/stats/devicepolicy/device_policy.proto
index f5f3e47..af30cf3 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/core/proto/android/stats/devicepolicy/device_policy.proto
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,11 @@
* limitations under the License.
*/
-package android.media.update;
+syntax = "proto2";
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 20d315f..8b66be3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -620,6 +620,8 @@
<protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" />
+ <protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -794,7 +796,8 @@
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:label="@string/permlab_sdcardRead"
android:description="@string/permdesc_sdcardRead"
- android:protectionLevel="normal" />
+ android:protectionLevel="dangerous"
+ android:permissionFlags="removed" />
<!-- Allows an application to write to external storage.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
@@ -814,7 +817,8 @@
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:label="@string/permlab_sdcardWrite"
android:description="@string/permdesc_sdcardWrite"
- android:protectionLevel="normal" />
+ android:protectionLevel="dangerous"
+ android:permissionFlags="removed" />
<!-- Runtime permission controlling access to the user's shared aural media
collection. -->
@@ -831,12 +835,6 @@
android:description="@string/permdesc_audioRead"
android:protectionLevel="dangerous" />
- <!-- Allows an application to modify the user's shared audio collection. -->
- <permission android:name="android.permission.WRITE_MEDIA_AUDIO"
- android:label="@string/permlab_audioWrite"
- android:description="@string/permdesc_audioWrite"
- android:protectionLevel="dangerous" />
-
<!-- Runtime permission controlling access to the user's shared visual media
collection, including images and videos. -->
<permission-group android:name="android.permission-group.MEDIA_VISUAL"
@@ -852,24 +850,12 @@
android:description="@string/permdesc_imagesRead"
android:protectionLevel="dangerous" />
- <!-- Allows an application to modify the user's shared images collection. -->
- <permission android:name="android.permission.WRITE_MEDIA_IMAGES"
- android:label="@string/permlab_imagesWrite"
- android:description="@string/permdesc_imagesWrite"
- android:protectionLevel="dangerous" />
-
<!-- Allows an application to read the user's shared video collection. -->
<permission android:name="android.permission.READ_MEDIA_VIDEO"
android:label="@string/permlab_videoRead"
android:description="@string/permdesc_videoRead"
android:protectionLevel="dangerous" />
- <!-- Allows an application to modify the user's shared video collection. -->
- <permission android:name="android.permission.WRITE_MEDIA_VIDEO"
- android:label="@string/permlab_videoWrite"
- android:description="@string/permdesc_videoWrite"
- android:protectionLevel="dangerous" />
-
<!-- Allows an application to access any geographic locations persisted in the
user's shared collection. -->
<permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
@@ -3024,6 +3010,13 @@
<permission android:name="android.permission.BIND_TV_INPUT"
android:protectionLevel="signature|privileged" />
+ <!-- Must be required by an {@link android.service.sms.FinancialSmsService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_FINANCIAL_SMS_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi
Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
to ensure that only the system can bind to it.
@@ -4201,12 +4194,34 @@
<!-- @SystemApi Allows modifying accessibility state.
@hide -->
<permission android:name="android.permission.MANAGE_ACCESSIBILITY"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup" />
+
+ <!-- @SystemApi Allows an app to grant a profile owner access to device identifiers.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS"
+ android:protectionLevel="signature" />
<!-- Allows financial apps to read filtered sms messages. -->
<permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
android:protectionLevel="signature|appop" />
+ <!-- @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"
@@ -4494,6 +4509,14 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.updates.ConversationActionsInstallReceiver"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.ACTION_UPDATE_CONVERSATION_ACTIONS" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
<receiver android:name="com.android.server.updates.CarrierIdInstallReceiver"
android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
diff --git a/core/res/res/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml
index 3c9f6ee..c024dbe 100644
--- a/core/res/res/layout/notification_material_action.xml
+++ b/core/res/res/layout/notification_material_action.xml
@@ -16,16 +16,14 @@
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
- style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ style="@android:style/NotificationAction"
android:id="@+id/action0"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_gravity="center"
android:gravity="start|center_vertical"
android:layout_marginStart="4dp"
- android:textColor="@color/notification_default_color"
android:singleLine="true"
android:textAlignment="viewStart"
android:ellipsize="end"
- android:background="@drawable/notification_material_action_background"
/>
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 07559f4..4258019 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -18,6 +18,7 @@
android:id="@+id/actions_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_action_list_margin_top"
android:layout_gravity="bottom">
<com.android.internal.widget.NotificationActionListLayout
android:id="@+id/actions"
@@ -27,6 +28,7 @@
android:orientation="horizontal"
android:gravity="center_vertical"
android:visibility="gone"
+ android:background="@color/notification_action_list_background_color"
>
<!-- actions will be added here -->
</com.android.internal.widget.NotificationActionListLayout>
diff --git a/core/res/res/layout/notification_material_action_tombstone.xml b/core/res/res/layout/notification_material_action_tombstone.xml
index 9fa7c6a..f165724 100644
--- a/core/res/res/layout/notification_material_action_tombstone.xml
+++ b/core/res/res/layout/notification_material_action_tombstone.xml
@@ -16,7 +16,7 @@
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
- style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ style="@android:style/NotificationTombstoneAction"
android:id="@+id/action0"
android:layout_width="wrap_content"
android:layout_height="48dp"
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index 9de8842..a2ad3b9 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -20,7 +20,7 @@
<!-- Color palette -->
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
- <item name="colorAccent">@color/white</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
<item name="colorControlNormal">?attr/textColorPrimary</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 605662a..918070c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7935,12 +7935,19 @@
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. -->
<attr name="settingsSliceUri" />
+ <!-- Indicates that this wallpaper service can support multiple engines to render on each
+ surface independently. An example use case is a multi-display set-up where the
+ wallpaper service can render surfaces to each of the connected displays. -->
+ <attr name="supportsMultipleDisplays" format="boolean" />
+
</declare-styleable>
<!-- Use <code>dream</code> as the root tag of the XML resource that
@@ -8798,6 +8805,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..d5eaa00 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 AndroidManifestPermission#usageRequired}
+ {@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 AndroidManifestPermission#usageRequired}
+ {@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 AndroidManifestPermission#usageRequired}
+ {@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 AndroidManifestPermission#usageRequired}
+ {@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/colors.xml b/core/res/res/values/colors.xml
index ffcd300..16c0744 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -146,10 +146,14 @@
<color name="notification_default_color">#757575</color> <!-- Gray 600 -->
+ <color name="notification_action_button_text_color">@color/notification_default_color</color>
+
<color name="notification_progress_background_color">@color/secondary_text_material_light</color>
<color name="notification_action_list">#ffeeeeee</color>
+ <color name="notification_action_list_background_color">@null</color>
+
<!-- Keyguard colors -->
<color name="keyguard_avatar_frame_color">#ffffffff</color>
<color name="keyguard_avatar_frame_shadow_color">#80000000</color>
@@ -211,6 +215,6 @@
<color name="floating_popup_divider_light">#E9E9E9</color>
<!-- Magnifier -->
- <color name="magnifier_color_overlay">#0EFFFFFF</color>
+ <color name="default_magnifier_color_overlay">#00FFFFFF</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 829d6f5..1d80961 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -364,7 +364,7 @@
<!-- Max number of Bluetooth tethering connections allowed. If this is
updated config_tether_dhcp_range has to be updated appropriately. -->
- <integer translateable="false" name="config_max_pan_devices">5</integer>
+ <integer translatable="false" name="config_max_pan_devices">5</integer>
<!-- Dhcp range (min, max) to use for tethering purposes -->
<string-array translatable="false" name="config_tether_dhcp_range">
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b65c0fd..f7b9961 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -200,6 +200,9 @@
<!-- The height of the notification action list -->
<dimen name="notification_action_list_height">60dp</dimen>
+ <!-- The margin of the notification action list at the top -->
+ <dimen name="notification_action_list_margin_top">0dp</dimen>
+
<!-- The height of the notification action list -->
<dimen name="notification_action_emphasized_height">48dp</dimen>
@@ -578,12 +581,13 @@
<dimen name="floating_toolbar_icon_text_spacing">8dp</dimen>
<!-- Magnifier dimensions -->
- <dimen name="magnifier_width">100dp</dimen>
- <dimen name="magnifier_height">48dp</dimen>
- <dimen name="magnifier_elevation">4dp</dimen>
- <dimen name="magnifier_vertical_offset">-42dp</dimen>
- <dimen name="magnifier_horizontal_offset">0dp</dimen>
- <item type="dimen" format="float" name="magnifier_zoom">1.25</item>
+ <dimen name="default_magnifier_width">100dp</dimen>
+ <dimen name="default_magnifier_height">48dp</dimen>
+ <dimen name="default_magnifier_elevation">4dp</dimen>
+ <dimen name="default_magnifier_corner_radius">2dp</dimen>
+ <dimen name="default_magnifier_vertical_offset">-42dp</dimen>
+ <dimen name="default_magnifier_horizontal_offset">0dp</dimen>
+ <item type="dimen" format="float" name="default_magnifier_zoom">1.25</item>
<dimen name="chooser_grid_padding">0dp</dimen>
<!-- Spacing around the background change frome service to non-service -->
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 73dae08..feefcad 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" />
@@ -2920,6 +2921,13 @@
<public name="shell" />
<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-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 200c35d..bd6d976 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1586,22 +1586,14 @@
<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] -->
- <string name="permlab_sdcardRead" product="nosdcard">read the contents of your USB storage</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_sdcardRead" product="default">read the contents of your SD card</string>
+ <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] -->
- <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to read the contents of your USB storage.</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_sdcardRead" product="default">Allows the app to read the contents of your SD card.</string>
+ <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] -->
- <string name="permlab_sdcardWrite" product="nosdcard">modify or delete the contents of your USB storage</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_sdcardWrite" product="default">modify or delete the contents of your SD card</string>
+ <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] -->
- <string name="permdesc_sdcardWrite" product="nosdcard">Allows the app to write to the USB storage.</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_sdcardWrite" product="default">Allows the app to write to the SD card.</string>
+ <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. -->
<string name="permlab_use_sip">make/receive SIP calls</string>
@@ -2989,6 +2981,18 @@
<!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] -->
<string name="view_flight_desc">Track selected flight</string>
+ <!-- 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>
+
+ <!-- Label for item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=30] -->
+ <string name="define">Define</string>
+
+ <!-- Accessibility description for an item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=NONE] -->
+ <string name="define_desc">Define selected text</string>
+
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
<string name="low_internal_storage_view_title">Storage space running out</string>
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. -->
@@ -3867,10 +3871,8 @@
<string name="action_mode_done">Done</string>
<!-- Strings for MasterClearReceiver. -->
- <!-- Text for progress dialog while erasing USB storage volume [CHAR LIMIT=NONE] -->
- <string name="progress_erasing" product="nosdcard">Erasing USB storage\u2026</string>
- <!-- Text for progress dialog while erasing SD card [CHAR LIMIT=NONE] -->
- <string name="progress_erasing" product="default">Erasing SD card\u2026</string>
+ <!-- Text for progress dialog while erasing the shared storage volume [CHAR LIMIT=NONE] -->
+ <string name="progress_erasing">Erasing shared storage\u2026</string>
<!-- Text for WebView's text selection Action Mode -->
<!-- ActionBar action to share the current selection [CHAR LIMIT=10] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index ec22f42..18f7e48 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -800,13 +800,13 @@
</style>
<style name="Widget.Magnifier">
- <item name="magnifierWidth">@dimen/magnifier_width</item>
- <item name="magnifierHeight">@dimen/magnifier_height</item>
- <item name="magnifierZoom">@dimen/magnifier_zoom</item>
- <item name="magnifierElevation">@dimen/magnifier_elevation</item>
- <item name="magnifierVerticalOffset">@dimen/magnifier_vertical_offset</item>
- <item name="magnifierHorizontalOffset">@dimen/magnifier_horizontal_offset</item>
- <item name="magnifierColorOverlay">@color/magnifier_color_overlay</item>
+ <item name="magnifierWidth">100dp</item>
+ <item name="magnifierHeight">48dp</item>
+ <item name="magnifierZoom">1.25</item>
+ <item name="magnifierElevation">4dp</item>
+ <item name="magnifierVerticalOffset">-42dp</item>
+ <item name="magnifierHorizontalOffset">0dp</item>
+ <item name="magnifierColorOverlay">#0EFFFFFF</item>
</style>
<!-- Text Appearances -->
@@ -1526,10 +1526,22 @@
<item name="gravity">top</item>
</style>
- <!-- Colored bordered ink button -->
+ <!-- The style for normal action button on notification -->
+ <style name="NotificationAction" parent="Widget.Material.Light.Button.Borderless.Small">
+ <item name="textColor">@color/notification_action_button_text_color</item>
+ <item name="background">@drawable/notification_material_action_background</item>
+ </style>
+
+ <!-- The style for emphasized action button on notification: Colored bordered ink button -->
<style name="NotificationEmphasizedAction" parent="Widget.Material.Button">
<item name="background">@drawable/btn_notification_emphasized</item>
<item name="stateListAnimator">@anim/flat_button_state_list_anim_material</item>
</style>
+ <!-- The style for disabled action button on notification -->
+ <style name="NotificationTombstoneAction" parent="NotificationAction">
+ <item name="textColor">#555555</item>
+ </style>
+
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b25e7a8..e251e27 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,10 @@
<java-symbol type="string" name="add_calendar_event_desc" />
<java-symbol type="string" name="view_flight" />
<java-symbol type="string" name="view_flight_desc" />
+ <java-symbol type="string" name="translate" />
+ <java-symbol type="string" name="translate_desc" />
+ <java-symbol type="string" name="define" />
+ <java-symbol type="string" name="define_desc" />
<java-symbol type="string" name="textSelectionCABTitle" />
<java-symbol type="string" name="BaMmi" />
<java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
@@ -2637,13 +2641,14 @@
<java-symbol type="attr" name="floatingToolbarDividerColor" />
<!-- Magnifier -->
- <java-symbol type="dimen" name="magnifier_width" />
- <java-symbol type="dimen" name="magnifier_height" />
- <java-symbol type="dimen" name="magnifier_elevation" />
- <java-symbol type="dimen" name="magnifier_zoom" />
- <java-symbol type="dimen" name="magnifier_vertical_offset" />
- <java-symbol type="dimen" name="magnifier_horizontal_offset" />
- <java-symbol type="color" name="magnifier_color_overlay" />
+ <java-symbol type="dimen" name="default_magnifier_width" />
+ <java-symbol type="dimen" name="default_magnifier_height" />
+ <java-symbol type="dimen" name="default_magnifier_elevation" />
+ <java-symbol type="dimen" name="default_magnifier_corner_radius" />
+ <java-symbol type="dimen" name="default_magnifier_zoom" />
+ <java-symbol type="dimen" name="default_magnifier_vertical_offset" />
+ <java-symbol type="dimen" name="default_magnifier_horizontal_offset" />
+ <java-symbol type="color" name="default_magnifier_color_overlay" />
<java-symbol type="attr" name="magnifierWidth" />
<java-symbol type="attr" name="magnifierHeight" />
<java-symbol type="attr" name="magnifierElevation" />
@@ -2678,6 +2683,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" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index f60d8d0ad..3b650e5 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1316,6 +1316,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/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index a317c99..8ac9451d 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -364,9 +364,7 @@
private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) {
// Flags explicitly over ride everything else.
- if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
- return INSTALL_LOC_SD;
- } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
+ if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
return INSTALL_LOC_INT;
}
// Manifest option takes precedence next
@@ -437,8 +435,6 @@
int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen);
if (rLoc == INSTALL_LOC_INT) {
- assertFalse(
- (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
assertEquals(appInstallPath, srcPath);
assertEquals(appInstallPath, publicSrcPath);
assertStartsWith("Native library should point to shared lib directory",
@@ -461,8 +457,6 @@
}
}
} else if (rLoc == INSTALL_LOC_SD) {
- assertFalse("The application should not be installed forward locked",
- (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
assertTrue("Application flags (" + info.flags
+ ") should contain FLAG_EXTERNAL_STORAGE",
(info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
@@ -845,31 +839,10 @@
}
@LargeTest
- public void testReplaceFailSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- sampleReplaceFromRawResource(PackageManager.INSTALL_EXTERNAL);
- }
-
- @LargeTest
public void testReplaceNormalInternal() throws Exception {
sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
}
- @LargeTest
- public void testReplaceSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING
- | PackageManager.INSTALL_EXTERNAL);
- }
-
/* -------------- Delete tests --- */
private static class DeleteObserver extends IPackageDeleteObserver.Stub {
private CountDownLatch mLatch = new CountDownLatch(1);
@@ -1015,31 +988,12 @@
deleteFromRawResource(0, 0);
}
- @LargeTest
- public void testDeleteSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, 0);
- }
@LargeTest
public void testDeleteNormalInternalRetainData() throws Exception {
deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
}
- @LargeTest
- public void testDeleteSdcardRetainData() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DELETE_KEEP_DATA);
- }
-
void cleanUpInstall(InstallParams ip) throws Exception {
if (ip == null) {
return;
@@ -1104,60 +1058,6 @@
0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
}
- /*
- * Install a package on internal flash via PackageManager install flag. Replace
- * the package via flag to install on sdcard. Make sure the new flag overrides
- * the old install location.
- */
- @LargeTest
- public void testReplaceFlagInternalSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = 0;
- int rFlags = PackageManager.INSTALL_EXTERNAL;
- InstallParams ip = sampleInstallFromRawResource(iFlags, false);
- GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
- int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
- try {
- invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
- assertInstall(ip.pkg, rFlags, ip.pkg.installLocation);
- } catch (Exception e) {
- failStr("Failed with exception : " + e);
- } finally {
- cleanUpInstall(ip);
- }
- }
-
- /*
- * Install a package on sdcard via PackageManager install flag. Replace
- * the package with no flags or manifest option and make sure the old
- * install location is retained.
- */
- @LargeTest
- public void testReplaceFlagSdcardInternal() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = 0;
- InstallParams ip = sampleInstallFromRawResource(iFlags, false);
- GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
- int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
- try {
- invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
- assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
- } catch (Exception e) {
- failStr("Failed with exception : " + e);
- } finally {
- cleanUpInstall(ip);
- }
- }
-
@LargeTest
public void testManifestInstallLocationReplaceInternalSdcard() throws Exception {
// Do not run on devices with emulated external storage.
@@ -1375,34 +1275,6 @@
}
@LargeTest
- public void testMoveAppExternalToExternal() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int installFlags = PackageManager.INSTALL_EXTERNAL;
- int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA;
- boolean fail = true;
- int result = PackageManager.MOVE_FAILED_INVALID_LOCATION;
- sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
- }
-
- @LargeTest
- public void testMoveAppExternalToInternal() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int installFlags = PackageManager.INSTALL_EXTERNAL;
- int moveFlags = PackageManager.MOVE_INTERNAL;
- boolean fail = false;
- int result = PackageManager.MOVE_SUCCEEDED;
- sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
- }
-
- @LargeTest
public void testMoveAppFailInternalToExternalDelete() throws Exception {
// Do not run on devices with emulated external storage.
if (Environment.isExternalStorageEmulated()) {
@@ -1458,19 +1330,6 @@
}
/*
- * Install an app on sdcard.
- */
- @LargeTest
- public void testFlagE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true);
- }
-
- /*
* Install an app with both internal and manifest option set.
* should install on internal.
*/
@@ -1506,59 +1365,6 @@
false, -1,
PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
- /*
- * Install an app with both external and manifest option set.
- * should install externally.
- */
- @LargeTest
- public void testFlagEManifestI() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- installFromRawResource("install.apk", R.raw.install_loc_internal,
- PackageManager.INSTALL_EXTERNAL,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- /*
- * Install an app with both external and manifest preference for
- * preferExternal. Should install externally.
- */
- @LargeTest
- public void testFlagEManifestE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- installFromRawResource("install.apk", R.raw.install_loc_sdcard,
- PackageManager.INSTALL_EXTERNAL,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- /*
- * Install an app with both external and manifest preference for
- * auto. should install on external media.
- */
- @LargeTest
- public void testFlagEManifestA() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- installFromRawResource("install.apk", R.raw.install_loc_auto,
- PackageManager.INSTALL_EXTERNAL,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
/*
* The following test functions verify install location for existing apps.
@@ -1586,75 +1392,6 @@
-1);
}
- @LargeTest
- public void testFlagIExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install,
- rFlags,
- true,
- false, -1,
- -1);
- }
-
- @LargeTest
- public void testFlagEExistingI() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_INTERNAL;
- int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install,
- rFlags,
- true,
- false, -1,
- -1);
- }
-
- @LargeTest
- public void testFlagEExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install,
- rFlags,
- true,
- false, -1,
- -1);
- }
-
/*
* The following set of tests verify the installation of apps with
* install location attribute set to internalOnly, preferExternal and auto.
@@ -1720,29 +1457,6 @@
}
@LargeTest
- public void testManifestIExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install_loc_internal,
- rFlags,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- }
-
- @LargeTest
public void testManifestEExistingI() throws Exception {
// Do not run on devices with emulated external storage.
if (Environment.isExternalStorageEmulated()) {
@@ -1766,29 +1480,6 @@
}
@LargeTest
- public void testManifestEExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install_loc_sdcard,
- rFlags,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- @LargeTest
public void testManifestAExistingI() throws Exception {
int iFlags = PackageManager.INSTALL_INTERNAL;
int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
@@ -1806,29 +1497,6 @@
PackageInfo.INSTALL_LOCATION_AUTO);
}
- @LargeTest
- public void testManifestAExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install_loc_auto,
- rFlags,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
/*
* The following set of tests check install location for existing
* application based on user setting.
@@ -1896,42 +1564,6 @@
setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
- @LargeTest
- public void testExistingEUserI() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- @LargeTest
- public void testExistingEUserE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- @LargeTest
- public void testExistingEUserA() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int userSetting = PackageHelper.APP_INSTALL_AUTO;
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
/*
* The following set of tests verify that the user setting defines
* the install location.
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/media/java/android/media/update/ProviderCreator.java b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
similarity index 73%
copy from media/java/android/media/update/ProviderCreator.java
copy to core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
index f5f3e47..365aebb 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package android.media.update;
+package android.os;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+interface IBinderWorkSourceNestedService {
+ int[] nestedCallWithWorkSourceToSet(int uidToBlame);
+ int[] nestedCall();
}
diff --git a/media/java/android/media/update/ProviderCreator.java b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
similarity index 74%
copy from media/java/android/media/update/ProviderCreator.java
copy to core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
index f5f3e47..05d4e82 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package android.media.update;
+package android.os;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+interface IBinderWorkSourceService {
+ int getIncomingWorkSourceUid();
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 002b6a8..305d2af 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -124,7 +124,9 @@
Settings.Global.AUTOFILL_LOGGING_LEVEL,
Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
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,
@@ -181,6 +183,8 @@
Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE,
Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
Settings.Global.CONTACT_METADATA_SYNC_ENABLED,
+ Settings.Global.CONVERSATION_ACTIONS_UPDATE_CONTENT_URL,
+ Settings.Global.CONVERSATION_ACTIONS_UPDATE_METADATA_URL,
Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
@@ -189,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,
@@ -281,6 +289,7 @@
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS,
Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
@@ -306,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,
@@ -653,7 +663,8 @@
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);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
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/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
new file mode 100644
index 0000000..f0faaf6
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Person;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.ActionsSuggestionsModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ActionsSuggestionsHelperTest {
+ @Test
+ public void testToNativeMessages_emptyInput() {
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(Collections.emptyList());
+
+ assertThat(conversationMessages).isEmpty();
+ }
+
+ @Test
+ public void testToNativeMessages_noTextMessages() {
+ ConversationActions.Message messageWithoutText =
+ new ConversationActions.Message.Builder().build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Collections.singletonList(messageWithoutText));
+
+ assertThat(conversationMessages).isEmpty();
+ }
+
+ @Test
+ public void testToNativeMessages_missingPersonInFirstMessage() {
+ ConversationActions.Message firstMessage =
+ new ConversationActions.Message.Builder()
+ .setText("first")
+ .build();
+ ConversationActions.Message secondMessage =
+ new ConversationActions.Message.Builder()
+ .setText("second")
+ .setAuthor(new Person.Builder().build())
+ .build();
+ ConversationActions.Message thirdMessage =
+ new ConversationActions.Message.Builder()
+ .setText("third")
+ .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
+ .build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Arrays.asList(firstMessage, secondMessage, thirdMessage));
+
+ assertThat(conversationMessages).hasLength(2);
+ assertNativeMessage(conversationMessages[0], secondMessage.getText(), 1);
+ assertNativeMessage(conversationMessages[1], thirdMessage.getText(), 0);
+ }
+
+ @Test
+ public void testToNativeMessages_missingPersonInMiddleOfConversation() {
+ ConversationActions.Message firstMessage =
+ new ConversationActions.Message.Builder()
+ .setText("first")
+ .setAuthor(new Person.Builder().setName("first").build())
+ .build();
+ ConversationActions.Message secondMessage =
+ new ConversationActions.Message.Builder()
+ .setText("second")
+ .build();
+ ConversationActions.Message thirdMessage =
+ new ConversationActions.Message.Builder()
+ .setText("third")
+ .setAuthor(new Person.Builder().setName("third").build())
+ .build();
+ ConversationActions.Message fourthMessage =
+ new ConversationActions.Message.Builder()
+ .setText("fourth")
+ .setAuthor(new Person.Builder().setName("fourth").build())
+ .build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+
+ assertThat(conversationMessages).hasLength(2);
+ assertNativeMessage(conversationMessages[0], thirdMessage.getText(), 2);
+ assertNativeMessage(conversationMessages[1], fourthMessage.getText(), 1);
+ }
+
+ @Test
+ public void testToNativeMessages_userIdEncoding() {
+ Person userA = new Person.Builder().setName("userA").build();
+ Person userB = new Person.Builder().setName("userB").build();
+
+ ConversationActions.Message firstMessage =
+ new ConversationActions.Message.Builder()
+ .setText("first")
+ .setAuthor(userB)
+ .build();
+ ConversationActions.Message secondMessage =
+ new ConversationActions.Message.Builder()
+ .setText("second")
+ .setAuthor(userA)
+ .build();
+ ConversationActions.Message thirdMessage =
+ new ConversationActions.Message.Builder()
+ .setText("third")
+ .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
+ .build();
+ ConversationActions.Message fourthMessage =
+ new ConversationActions.Message.Builder()
+ .setText("fourth")
+ .setAuthor(userA)
+ .build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+
+ assertThat(conversationMessages).hasLength(4);
+ assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2);
+ assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1);
+ assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0);
+ assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1);
+ }
+
+ private static void assertNativeMessage(
+ ActionsSuggestionsModel.ConversationMessage nativeMessage,
+ CharSequence text,
+ int userId) {
+ assertThat(nativeMessage.getText()).isEqualTo(text.toString());
+ assertThat(nativeMessage.getUserId()).isEqualTo(userId);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
new file mode 100644
index 0000000..0180856
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
@@ -0,0 +1,134 @@
+/*
+ * 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.textclassifier;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.base.Preconditions;
+
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * A builder used to build a fake context for testing.
+ */
+// TODO: Consider making public.
+final class FakeContextBuilder {
+
+ /**
+ * A component name that can be used for tests.
+ */
+ public static final ComponentName DEFAULT_COMPONENT = new ComponentName("pkg", "cls");
+
+ private final PackageManager mPackageManager;
+ private final ContextWrapper mContext;
+ private final Map<String, ComponentName> mComponents = new HashMap<>();
+ private @Nullable ComponentName mAllIntentComponent;
+
+ FakeContextBuilder() {
+ mPackageManager = mock(PackageManager.class);
+ when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(null);
+ mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) {
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+ };
+ }
+
+ /**
+ * Sets the component name of an activity to handle the specified intent action.
+ * <p>
+ * <strong>NOTE: </strong>By default, no component is set to handle any intent.
+ */
+ public FakeContextBuilder setIntentComponent(
+ String intentAction, @Nullable ComponentName component) {
+ Preconditions.checkNotNull(intentAction);
+ mComponents.put(intentAction, component);
+ return this;
+ }
+
+
+ /**
+ * Sets the component name of an activity to handle all intents.
+ * <p>
+ * <strong>NOTE: </strong>By default, no component is set to handle any intent.
+ */
+ public FakeContextBuilder setAllIntentComponent(@Nullable ComponentName component) {
+ mAllIntentComponent = component;
+ return this;
+ }
+
+ /**
+ * Builds and returns a fake context.
+ */
+ public Context build() {
+ when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenAnswer(
+ (Answer<ResolveInfo>) invocation -> {
+ final String action = ((Intent) invocation.getArgument(0)).getAction();
+ final ComponentName component = mComponents.containsKey(action)
+ ? mComponents.get(action)
+ : mAllIntentComponent;
+ return getResolveInfo(component);
+ });
+ return mContext;
+ }
+
+ /**
+ * Returns a component name with random package and class names.
+ */
+ public static ComponentName newComponent() {
+ return new ComponentName(UUID.randomUUID().toString(), UUID.randomUUID().toString());
+ }
+
+ private static ResolveInfo getResolveInfo(ComponentName component) {
+ final ResolveInfo info;
+ if (component == null) {
+ info = null;
+ } else {
+ // NOTE: If something breaks in TextClassifier because we expect more fields to be set
+ // in here, just add them.
+ info = new ResolveInfo();
+ info.activityInfo = new ActivityInfo();
+ info.activityInfo.packageName = component.getPackageName();
+ info.activityInfo.name = component.getClassName();
+ info.activityInfo.exported = true;
+ info.activityInfo.applicationInfo = new ApplicationInfo();
+ info.activityInfo.applicationInfo.icon = 0;
+ }
+ return info;
+ }
+
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
new file mode 100644
index 0000000..bae2be3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.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 android.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.AnnotatorModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IntentFactoryTest {
+
+ private static final String TEXT = "text";
+
+ @Test
+ public void create_typeDictionary() {
+ AnnotatorModel.ClassificationResult classificationResult =
+ new AnnotatorModel.ClassificationResult(
+ TextClassifier.TYPE_DICTIONARY,
+ 1.0f,
+ null,
+ null);
+
+ List<TextClassifierImpl.LabeledIntent> intents = TextClassifierImpl.IntentFactory.create(
+ InstrumentationRegistry.getContext(),
+ TEXT,
+ false,
+ null,
+ classificationResult);
+
+ assertThat(intents).hasSize(1);
+ TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
+ Intent intent = labeledIntent.getIntent();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
+ assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 3a33d57..a3f69d9 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -16,340 +16,37 @@
package android.view.textclassifier;
-import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
import android.os.LocaleList;
import android.service.textclassifier.TextClassifierService;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextClassificationManagerTest {
private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
- private static final String NO_TYPE = null;
private Context mContext;
private TextClassificationManager mTcm;
- private TextClassifier mClassifier;
@Before
public void setup() {
mContext = InstrumentationRegistry.getTargetContext();
mTcm = mContext.getSystemService(TextClassificationManager.class);
- // Test with the local textClassifier only. (We only bundle "en" model by default).
- // It's hard to reliably test the results of the device's TextClassifierServiceImpl here.
- mClassifier = mTcm.getTextClassifier(TextClassifier.LOCAL);
- }
-
- @Test
- public void testSmartSelection() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Contact me at droid@android.com";
- String selected = "droid";
- String suggested = "droid@android.com";
- int startIndex = text.indexOf(selected);
- int endIndex = startIndex + selected.length();
- int smartStartIndex = text.indexOf(suggested);
- int smartEndIndex = smartStartIndex + suggested.length();
- TextSelection.Request request = new TextSelection.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextSelection selection = mClassifier.suggestSelection(request);
- assertThat(selection,
- isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
- }
-
- @Test
- public void testSmartSelection_url() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Visit http://www.android.com for more information";
- String selected = "http";
- String suggested = "http://www.android.com";
- int startIndex = text.indexOf(selected);
- int endIndex = startIndex + selected.length();
- int smartStartIndex = text.indexOf(suggested);
- int smartEndIndex = smartStartIndex + suggested.length();
- TextSelection.Request request = new TextSelection.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextSelection selection = mClassifier.suggestSelection(request);
- assertThat(selection,
- isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
- }
-
- @Test
- public void testSmartSelection_withEmoji() {
- if (isTextClassifierDisabled()) return;
-
- String text = "\uD83D\uDE02 Hello.";
- String selected = "Hello";
- int startIndex = text.indexOf(selected);
- int endIndex = startIndex + selected.length();
- TextSelection.Request request = new TextSelection.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextSelection selection = mClassifier.suggestSelection(request);
- assertThat(selection,
- isTextSelection(startIndex, endIndex, NO_TYPE));
- }
-
- @Test
- public void testClassifyText() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Contact me at droid@android.com";
- String classifiedText = "droid@android.com";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_EMAIL));
- }
-
- @Test
- public void testTextClassifyText_url() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Visit www.android.com for more information";
- String classifiedText = "www.android.com";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
- }
-
- @Test
- public void testTextClassifyText_address() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Brandschenkestrasse 110, Zürich, Switzerland";
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, 0, text.length())
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(text, TextClassifier.TYPE_ADDRESS));
- }
-
- @Test
- public void testTextClassifyText_url_inCaps() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Visit HTTP://ANDROID.COM for more information";
- String classifiedText = "HTTP://ANDROID.COM";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
- }
-
- @Test
- public void testTextClassifyText_date() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Let's meet on January 9, 2018.";
- String classifiedText = "January 9, 2018";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_DATE));
- }
-
- @Test
- public void testTextClassifyText_datetime() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Let's meet 2018/01/01 10:30:20.";
- String classifiedText = "2018/01/01 10:30:20";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification,
- isTextClassification(classifiedText, TextClassifier.TYPE_DATE_TIME));
- }
-
- @Test
- public void testGenerateLinks_phone() {
- if (isTextClassifierDisabled()) return;
- String text = "The number is +12122537077. See you tonight!";
- TextLinks.Request request = new TextLinks.Request.Builder(text).build();
- assertThat(mClassifier.generateLinks(request),
- isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE));
- }
-
- @Test
- public void testGenerateLinks_exclude() {
- if (isTextClassifierDisabled()) return;
- String text = "You want apple@banana.com. See you tonight!";
- List<String> hints = Collections.EMPTY_LIST;
- List<String> included = Collections.EMPTY_LIST;
- List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
- TextLinks.Request request = new TextLinks.Request.Builder(text)
- .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
- .setDefaultLocales(LOCALES)
- .build();
- assertThat(mClassifier.generateLinks(request),
- not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
- }
-
- @Test
- public void testGenerateLinks_explicit_address() {
- if (isTextClassifierDisabled()) return;
- String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
- List<String> explicit = Arrays.asList(TextClassifier.TYPE_ADDRESS);
- TextLinks.Request request = new TextLinks.Request.Builder(text)
- .setEntityConfig(TextClassifier.EntityConfig.createWithExplicitEntityList(explicit))
- .setDefaultLocales(LOCALES)
- .build();
- assertThat(mClassifier.generateLinks(request),
- isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
- TextClassifier.TYPE_ADDRESS));
- }
-
- @Test
- public void testGenerateLinks_exclude_override() {
- if (isTextClassifierDisabled()) return;
- String text = "You want apple@banana.com. See you tonight!";
- List<String> hints = Collections.EMPTY_LIST;
- List<String> included = Arrays.asList(TextClassifier.TYPE_EMAIL);
- List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
- TextLinks.Request request = new TextLinks.Request.Builder(text)
- .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
- .setDefaultLocales(LOCALES)
- .build();
- assertThat(mClassifier.generateLinks(request),
- not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
- }
-
- @Test
- public void testGenerateLinks_maxLength() {
- if (isTextClassifierDisabled()) return;
- char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength()];
- Arrays.fill(manySpaces, ' ');
- TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
- TextLinks links = mClassifier.generateLinks(request);
- assertTrue(links.getLinks().isEmpty());
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testGenerateLinks_tooLong() {
- if (isTextClassifierDisabled()) {
- throw new IllegalArgumentException("pass if disabled");
- }
- char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
- Arrays.fill(manySpaces, ' ');
- TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
- mClassifier.generateLinks(request);
- }
-
- @Test
- public void testDetectLanguage() {
- if (isTextClassifierDisabled()) return;
- String text = "This is English text";
- TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
- TextLanguage textLanguage = mClassifier.detectLanguage(request);
- assertThat(textLanguage, isTextLanguage("en"));
- }
-
- @Test
- public void testDetectLanguage_japanese() {
- if (isTextClassifierDisabled()) return;
- String text = "これは日本語のテキストです";
- TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
- TextLanguage textLanguage = mClassifier.detectLanguage(request);
- assertThat(textLanguage, isTextLanguage("ja"));
- }
-
- @Test
- public void testSuggestConversationActions_textReplyOnly_maxThree() {
- if (isTextClassifierDisabled()) return;
- ConversationActions.Message message =
- new ConversationActions.Message.Builder().setText("Hello").build();
- ConversationActions.TypeConfig typeConfig =
- new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
- .setIncludedTypes(
- Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setMaxSuggestions(1)
- .setTypeConfig(typeConfig)
- .build();
-
- ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
- assertTrue(conversationActions.getConversationActions().size() <= 1);
- for (ConversationActions.ConversationAction conversationAction :
- conversationActions.getConversationActions()) {
- assertEquals(conversationAction.getType(), ConversationActions.TYPE_TEXT_REPLY);
- assertNotNull(conversationAction.getTextReply());
- assertTrue(conversationAction.getConfidenceScore() > 0);
- assertTrue(conversationAction.getConfidenceScore() <= 1);
- }
}
@Test
@@ -372,23 +69,10 @@
@Test
public void testCannotResolveIntent() {
- final PackageManager fakePackageMgr = mock(PackageManager.class);
-
- ResolveInfo validInfo = mContext.getPackageManager().resolveActivity(
- new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:+12122537077")), 0);
- // Make packageManager fail when it gets the following intent:
- ArgumentMatcher<Intent> toFailIntent =
- intent -> intent.getAction().equals(Intent.ACTION_INSERT_OR_EDIT);
-
- when(fakePackageMgr.resolveActivity(any(Intent.class), anyInt())).thenReturn(validInfo);
- when(fakePackageMgr.resolveActivity(argThat(toFailIntent), anyInt())).thenReturn(null);
-
- ContextWrapper fakeContext = new ContextWrapper(mContext) {
- @Override
- public PackageManager getPackageManager() {
- return fakePackageMgr;
- }
- };
+ Context fakeContext = new FakeContextBuilder()
+ .setAllIntentComponent(FakeContextBuilder.DEFAULT_COMPONENT)
+ .setIntentComponent(Intent.ACTION_INSERT_OR_EDIT, null)
+ .build();
TextClassifier fallback = TextClassifier.NO_OP;
TextClassifier classifier = new TextClassifierImpl(
@@ -411,102 +95,4 @@
assertFalse(result.getActions().isEmpty());
assertNotSame(result, fallbackResult);
}
-
- private boolean isTextClassifierDisabled() {
- return mClassifier == TextClassifier.NO_OP;
- }
-
- private static Matcher<TextSelection> isTextSelection(
- final int startIndex, final int endIndex, final String type) {
- return new BaseMatcher<TextSelection>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextSelection) {
- TextSelection selection = (TextSelection) o;
- return startIndex == selection.getSelectionStartIndex()
- && endIndex == selection.getSelectionEndIndex()
- && typeMatches(selection, type);
- }
- return false;
- }
-
- private boolean typeMatches(TextSelection selection, String type) {
- return type == null
- || (selection.getEntityCount() > 0
- && type.trim().equalsIgnoreCase(selection.getEntity(0)));
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendValue(
- String.format("%d, %d, %s", startIndex, endIndex, type));
- }
- };
- }
-
- private static Matcher<TextLinks> isTextLinksContaining(
- final String text, final String substring, final String type) {
- return new BaseMatcher<TextLinks>() {
-
- @Override
- public void describeTo(Description description) {
- description.appendText("text=").appendValue(text)
- .appendText(", substring=").appendValue(substring)
- .appendText(", type=").appendValue(type);
- }
-
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextLinks) {
- for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) {
- if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) {
- return type.equals(link.getEntity(0));
- }
- }
- }
- return false;
- }
- };
- }
-
- private static Matcher<TextClassification> isTextClassification(
- final String text, final String type) {
- return new BaseMatcher<TextClassification>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextClassification) {
- TextClassification result = (TextClassification) o;
- return text.equals(result.getText())
- && result.getEntityCount() > 0
- && type.equals(result.getEntity(0));
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("text=").appendValue(text)
- .appendText(", type=").appendValue(type);
- }
- };
- }
-
- private static Matcher<TextLanguage> isTextLanguage(final String languageTag) {
- return new BaseMatcher<TextLanguage>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextLanguage) {
- TextLanguage result = (TextLanguage) o;
- return result.getLocaleHypothesisCount() > 0
- && languageTag.equals(result.getLocale(0).toLanguageTag());
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("locale=").appendValue(languageTag);
- }
- };
- }
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
new file mode 100644
index 0000000..fbcb629
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -0,0 +1,498 @@
+/*
+ * 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.textclassifier;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+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;
+import android.text.Spannable;
+import android.text.SpannableString;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Testing {@link TextClassifierTest} APIs on local and system textclassifier.
+ * <p>
+ * Tests are skipped if such a textclassifier does not exist.
+ */
+@SmallTest
+@RunWith(Parameterized.class)
+public class TextClassifierTest {
+ private static final String LOCAL = "local";
+ private static final String SYSTEM = "system";
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object> textClassifierTypes() {
+ return Arrays.asList(LOCAL);
+
+ // TODO: The following will fail on any device that specifies a no-op TextClassifierService.
+ // Enable when we can set a specified TextClassifierService for testing.
+ // return Arrays.asList(LOCAL, SYSTEM);
+ }
+
+ @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;
+
+ private Context mContext;
+ private TextClassificationManager mTcm;
+ private TextClassifier mClassifier;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mTcm = mContext.getSystemService(TextClassificationManager.class);
+ mClassifier = mTcm.getTextClassifier(
+ mTextClassifierType.equals(LOCAL) ? TextClassifier.LOCAL : TextClassifier.SYSTEM);
+ }
+
+ @Test
+ public void testSuggestSelection() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Contact me at droid@android.com";
+ String selected = "droid";
+ String suggested = "droid@android.com";
+ int startIndex = text.indexOf(selected);
+ int endIndex = startIndex + selected.length();
+ int smartStartIndex = text.indexOf(suggested);
+ int smartEndIndex = smartStartIndex + suggested.length();
+ TextSelection.Request request = new TextSelection.Request.Builder(
+ text, startIndex, endIndex)
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextSelection selection = mClassifier.suggestSelection(request);
+ assertThat(selection,
+ isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
+ }
+
+ @Test
+ public void testSuggestSelection_url() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Visit http://www.android.com for more information";
+ String selected = "http";
+ String suggested = "http://www.android.com";
+ int startIndex = text.indexOf(selected);
+ int endIndex = startIndex + selected.length();
+ int smartStartIndex = text.indexOf(suggested);
+ int smartEndIndex = smartStartIndex + suggested.length();
+ TextSelection.Request request = new TextSelection.Request.Builder(
+ text, startIndex, endIndex)
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextSelection selection = mClassifier.suggestSelection(request);
+ assertThat(selection,
+ isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
+ }
+
+ @Test
+ public void testSmartSelection_withEmoji() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "\uD83D\uDE02 Hello.";
+ String selected = "Hello";
+ int startIndex = text.indexOf(selected);
+ int endIndex = startIndex + selected.length();
+ TextSelection.Request request = new TextSelection.Request.Builder(
+ text, startIndex, endIndex)
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextSelection selection = mClassifier.suggestSelection(request);
+ assertThat(selection,
+ isTextSelection(startIndex, endIndex, NO_TYPE));
+ }
+
+ @Test
+ public void testClassifyText() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Contact me at droid@android.com";
+ String classifiedText = "droid@android.com";
+ int startIndex = text.indexOf(classifiedText);
+ int endIndex = startIndex + classifiedText.length();
+ TextClassification.Request request = new TextClassification.Request.Builder(
+ text, startIndex, endIndex)
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextClassification classification = mClassifier.classifyText(request);
+ assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_EMAIL));
+ }
+
+ @Test
+ public void testClassifyText_url() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Visit www.android.com for more information";
+ String classifiedText = "www.android.com";
+ int startIndex = text.indexOf(classifiedText);
+ int endIndex = startIndex + classifiedText.length();
+ TextClassification.Request request = new TextClassification.Request.Builder(
+ text, startIndex, endIndex)
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextClassification classification = mClassifier.classifyText(request);
+ assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
+ }
+
+ @Test
+ public void testClassifyText_address() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Brandschenkestrasse 110, Zürich, Switzerland";
+ TextClassification.Request request = new TextClassification.Request.Builder(
+ text, 0, text.length())
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextClassification classification = mClassifier.classifyText(request);
+ assertThat(classification, isTextClassification(text, TextClassifier.TYPE_ADDRESS));
+ }
+
+ @Test
+ public void testClassifyText_url_inCaps() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Visit HTTP://ANDROID.COM for more information";
+ String classifiedText = "HTTP://ANDROID.COM";
+ int startIndex = text.indexOf(classifiedText);
+ int endIndex = startIndex + classifiedText.length();
+ TextClassification.Request request = new TextClassification.Request.Builder(
+ text, startIndex, endIndex)
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextClassification classification = mClassifier.classifyText(request);
+ assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
+ }
+
+ @Test
+ public void testClassifyText_date() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Let's meet on January 9, 2018.";
+ String classifiedText = "January 9, 2018";
+ int startIndex = text.indexOf(classifiedText);
+ int endIndex = startIndex + classifiedText.length();
+ TextClassification.Request request = new TextClassification.Request.Builder(
+ text, startIndex, endIndex)
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextClassification classification = mClassifier.classifyText(request);
+ assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_DATE));
+ }
+
+ @Test
+ public void testClassifyText_datetime() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Let's meet 2018/01/01 10:30:20.";
+ String classifiedText = "2018/01/01 10:30:20";
+ int startIndex = text.indexOf(classifiedText);
+ int endIndex = startIndex + classifiedText.length();
+ TextClassification.Request request = new TextClassification.Request.Builder(
+ text, startIndex, endIndex)
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextClassification classification = mClassifier.classifyText(request);
+ assertThat(classification,
+ isTextClassification(classifiedText, TextClassifier.TYPE_DATE_TIME));
+ }
+
+ @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!";
+ TextLinks.Request request = new TextLinks.Request.Builder(text).build();
+ assertThat(mClassifier.generateLinks(request),
+ isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE));
+ }
+
+ @Test
+ public void testGenerateLinks_exclude() {
+ if (isTextClassifierDisabled()) return;
+ String text = "You want apple@banana.com. See you tonight!";
+ List<String> hints = Collections.EMPTY_LIST;
+ List<String> included = Collections.EMPTY_LIST;
+ List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
+ TextLinks.Request request = new TextLinks.Request.Builder(text)
+ .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
+ .setDefaultLocales(LOCALES)
+ .build();
+ assertThat(mClassifier.generateLinks(request),
+ not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
+ }
+
+ @Test
+ public void testGenerateLinks_explicit_address() {
+ if (isTextClassifierDisabled()) return;
+ String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
+ List<String> explicit = Arrays.asList(TextClassifier.TYPE_ADDRESS);
+ TextLinks.Request request = new TextLinks.Request.Builder(text)
+ .setEntityConfig(TextClassifier.EntityConfig.createWithExplicitEntityList(explicit))
+ .setDefaultLocales(LOCALES)
+ .build();
+ assertThat(mClassifier.generateLinks(request),
+ isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
+ TextClassifier.TYPE_ADDRESS));
+ }
+
+ @Test
+ public void testGenerateLinks_exclude_override() {
+ if (isTextClassifierDisabled()) return;
+ String text = "You want apple@banana.com. See you tonight!";
+ List<String> hints = Collections.EMPTY_LIST;
+ List<String> included = Arrays.asList(TextClassifier.TYPE_EMAIL);
+ List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
+ TextLinks.Request request = new TextLinks.Request.Builder(text)
+ .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
+ .setDefaultLocales(LOCALES)
+ .build();
+ assertThat(mClassifier.generateLinks(request),
+ not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
+ }
+
+ @Test
+ public void testGenerateLinks_maxLength() {
+ if (isTextClassifierDisabled()) return;
+ char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength()];
+ Arrays.fill(manySpaces, ' ');
+ TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
+ TextLinks links = mClassifier.generateLinks(request);
+ assertTrue(links.getLinks().isEmpty());
+ }
+
+ @Test
+ public void testApplyLinks_unsupportedCharacter() {
+ if (isTextClassifierDisabled()) return;
+ Spannable url = new SpannableString("\u202Emoc.diordna.com");
+ TextLinks.Request request = new TextLinks.Request.Builder(url).build();
+ assertEquals(
+ TextLinks.STATUS_UNSUPPORTED_CHARACTER,
+ mClassifier.generateLinks(request).apply(url, 0, null));
+ }
+
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGenerateLinks_tooLong() {
+ if (isTextClassifierDisabled()) {
+ throw new IllegalArgumentException("pass if disabled");
+ }
+ char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
+ Arrays.fill(manySpaces, ' ');
+ TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
+ mClassifier.generateLinks(request);
+ }
+
+ @Test
+ public void testDetectLanguage() {
+ if (isTextClassifierDisabled()) return;
+ String text = "This is English text";
+ TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
+ TextLanguage textLanguage = mClassifier.detectLanguage(request);
+ assertThat(textLanguage, isTextLanguage("en"));
+ }
+
+ @Test
+ public void testDetectLanguage_japanese() {
+ if (isTextClassifierDisabled()) return;
+ String text = "これは日本語のテキストです";
+ TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
+ TextLanguage textLanguage = mClassifier.detectLanguage(request);
+ assertThat(textLanguage, isTextLanguage("ja"));
+ }
+
+ @Test
+ public void testSuggestConversationActions_textReplyOnly_maxThree() {
+ if (isTextClassifierDisabled()) return;
+ ConversationActions.Message message =
+ new ConversationActions.Message.Builder().setText("Hello").build();
+ ConversationActions.TypeConfig typeConfig =
+ new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
+ .setIncludedTypes(
+ Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+ .build();
+ ConversationActions.Request request =
+ new ConversationActions.Request.Builder(Collections.singletonList(message))
+ .setMaxSuggestions(3)
+ .setTypeConfig(typeConfig)
+ .build();
+
+ ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
+ assertTrue(conversationActions.getConversationActions().size() > 0);
+ assertTrue(conversationActions.getConversationActions().size() <= 3);
+ for (ConversationActions.ConversationAction conversationAction :
+ conversationActions.getConversationActions()) {
+ assertEquals(conversationAction.getType(), ConversationActions.TYPE_TEXT_REPLY);
+ assertNotNull(conversationAction.getTextReply());
+ assertTrue(conversationAction.getConfidenceScore() > 0);
+ assertTrue(conversationAction.getConfidenceScore() <= 1);
+ }
+ }
+
+
+ private boolean isTextClassifierDisabled() {
+ return mClassifier == null || mClassifier == TextClassifier.NO_OP;
+ }
+
+ private static Matcher<TextSelection> isTextSelection(
+ final int startIndex, final int endIndex, final String type) {
+ return new BaseMatcher<TextSelection>() {
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof TextSelection) {
+ TextSelection selection = (TextSelection) o;
+ return startIndex == selection.getSelectionStartIndex()
+ && endIndex == selection.getSelectionEndIndex()
+ && typeMatches(selection, type);
+ }
+ return false;
+ }
+
+ private boolean typeMatches(TextSelection selection, String type) {
+ return type == null
+ || (selection.getEntityCount() > 0
+ && type.trim().equalsIgnoreCase(selection.getEntity(0)));
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendValue(
+ String.format("%d, %d, %s", startIndex, endIndex, type));
+ }
+ };
+ }
+
+ private static Matcher<TextLinks> isTextLinksContaining(
+ final String text, final String substring, final String type) {
+ return new BaseMatcher<TextLinks>() {
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("text=").appendValue(text)
+ .appendText(", substring=").appendValue(substring)
+ .appendText(", type=").appendValue(type);
+ }
+
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof TextLinks) {
+ for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) {
+ if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) {
+ return type.equals(link.getEntity(0));
+ }
+ }
+ }
+ return false;
+ }
+ };
+ }
+
+ private static Matcher<TextClassification> isTextClassification(
+ final String text, final String type) {
+ return new BaseMatcher<TextClassification>() {
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof TextClassification) {
+ TextClassification result = (TextClassification) o;
+ return text.equals(result.getText())
+ && result.getEntityCount() > 0
+ && type.equals(result.getEntity(0));
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("text=").appendValue(text)
+ .appendText(", type=").appendValue(type);
+ }
+ };
+ }
+
+ private static Matcher<TextLanguage> isTextLanguage(final String languageTag) {
+ return new BaseMatcher<TextLanguage>() {
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof TextLanguage) {
+ TextLanguage result = (TextLanguage) o;
+ return result.getLocaleHypothesisCount() > 0
+ && languageTag.equals(result.getLocale(0).toLanguageTag());
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("locale=").appendValue(languageTag);
+ }
+ };
+ }
+}
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/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 70dc618..90758ba 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -983,6 +983,19 @@
}
@Test
+ public void testNoAssistItemForTextFieldWithUnsupportedCharacters() throws Throwable {
+ useSystemDefaultTextClassifier();
+ final String text = "\u202Emoc.diordna.com";
+ final TextView textView = mActivity.findViewById(R.id.textview);
+ mActivityRule.runOnUiThread(() -> textView.setText(text));
+ mInstrumentation.waitForIdleSync();
+
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('.')));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
+ }
+
+ @Test
public void testSelectionMetricsLogger_noAbandonAfterCopy() throws Throwable {
final List<SelectionEvent> selectionEvents = new ArrayList<>();
final TextClassifier classifier = new TextClassifier() {
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/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 3cfc644..225515e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -39,6 +39,10 @@
BatteryStatsUserLifecycleTests.class,
KernelCpuProcReaderTest.class,
KernelCpuProcStringReaderTest.class,
+ KernelCpuUidActiveTimeReaderTest.class,
+ KernelCpuUidClusterTimeReaderTest.class,
+ KernelCpuUidFreqTimeReaderTest.class,
+ KernelCpuUidUserSysTimeReaderTest.class,
KernelMemoryBandwidthStatsTest.class,
KernelSingleUidTimeReaderTest.class,
KernelUidCpuFreqTimeReaderTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index dae9eb5..2663f2b 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -37,6 +37,7 @@
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.nio.CharBuffer;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
@@ -149,7 +150,7 @@
+ "0 0 1 1 1 0 2 0 221",
iter.nextLine().toString());
long[] actual = new long[43];
- iter.nextLineAsArray(actual);
+ KernelCpuProcStringReader.asLongs(iter.nextLine(), actual);
assertArrayEquals(
new long[]{50227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 721},
@@ -183,7 +184,7 @@
}
}
- /** Tests nextLineToArray functionality. */
+ /** Tests reading lines, then converting to long[]. */
@Test
public void testReadLineToArray() throws Exception {
final long[][] data = getTestArray(800, 50);
@@ -193,12 +194,32 @@
long[] actual = new long[50];
try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
for (long[] expected : data) {
- assertEquals(50, iter.nextLineAsArray(actual));
+ CharBuffer cb = iter.nextLine();
+ String before = cb.toString();
+ assertEquals(50, KernelCpuProcStringReader.asLongs(cb, actual));
assertArrayEquals(expected, actual);
+ assertEquals("Buffer not reset to the pos before reading", before, cb.toString());
}
}
}
+ /** Tests error handling of converting to long[]. */
+ @Test
+ public void testLineToArrayErrorHandling() {
+ long[] actual = new long[100];
+ String invalidChar = "123: -1234 456";
+ String overflow = "123: 999999999999999999999999999999999999999999999999999999999 123";
+ CharBuffer cb = CharBuffer.wrap("----" + invalidChar + "+++", 4, 4 + invalidChar.length());
+ assertEquals("Failed to report err for: " + invalidChar, -2,
+ KernelCpuProcStringReader.asLongs(cb, actual));
+ assertEquals("Buffer not reset to the same pos before reading", invalidChar, cb.toString());
+
+ cb = CharBuffer.wrap("----" + overflow + "+++", 4, 4 + overflow.length());
+ assertEquals("Failed to report err for: " + overflow, -3,
+ KernelCpuProcStringReader.asLongs(cb, actual));
+ assertEquals("Buffer not reset to the pos before reading", overflow, cb.toString());
+ }
+
/**
* Tests that reading a file over the limit (1MB) will return null.
*/
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 c866bc4..385bad5 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -39,13 +39,18 @@
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)
public class KernelCpuThreadReaderTest {
- private static final String PROCESS_NAME = "test_process";
+ private static final int UID = 1000;
+ private static final int PROCESS_ID = 1234;
private static final int[] THREAD_IDS = {0, 1000, 1235, 4321};
+ private static final String PROCESS_NAME = "test_process";
private static final String[] THREAD_NAMES = {
"test_thread_1", "test_thread_2", "test_thread_3", "test_thread_4"
};
@@ -53,8 +58,8 @@
1000, 2000, 3000, 4000,
};
private static final int[][] THREAD_CPU_TIMES = {
- {1, 0, 0, 1},
- {0, 0, 0, 0},
+ {100, 0, 0, 100},
+ {0, 0, 9999999, 0},
{1000, 1000, 1000, 1000},
{0, 1, 2, 3},
};
@@ -73,49 +78,162 @@
}
@Test
- public void testSimple() throws IOException {
- // Make /proc/self
- final Path selfPath = mProcDirectory.toPath().resolve("self");
- assertTrue(selfPath.toFile().mkdirs());
+ public void testReader_currentProcess() throws IOException {
+ KernelCpuThreadReader.Injector processUtils =
+ new KernelCpuThreadReader.Injector() {
+ @Override
+ public int myPid() {
+ return PROCESS_ID;
+ }
- // Make /proc/self/task
- final Path selfThreadsPath = selfPath.resolve("task");
+ @Override
+ public int myUid() {
+ return UID;
+ }
+
+ @Override
+ public int getUidForPid(int pid) {
+ return 0;
+ }
+ };
+ setupDirectory(mProcDirectory.toPath().resolve("self"), THREAD_IDS, PROCESS_NAME,
+ THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
+
+ final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+ mProcDirectory.toPath(),
+ mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"),
+ processUtils);
+ final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+ kernelCpuThreadReader.getCurrentProcessCpuUsage();
+ checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(), UID, PROCESS_ID,
+ THREAD_IDS, PROCESS_NAME, THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
+ }
+
+ @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;
+ int[] expectedUids = new int[]{0, 4, 5, 6000};
+ KernelCpuThreadReader.Injector processUtils =
+ new KernelCpuThreadReader.Injector() {
+ @Override
+ public int myPid() {
+ return 0;
+ }
+
+ @Override
+ public int myUid() {
+ return 0;
+ }
+
+ @Override
+ public int getUidForPid(int pid) {
+ return pid;
+ }
+ };
+
+ for (int uid : uids) {
+ setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)),
+ new int[]{uid * 10},
+ "process" + uid, new String[]{"thread" + uid}, new int[]{1000},
+ new int[][]{{uid + 100}});
+ }
+ final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+ mProcDirectory.toPath(),
+ mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
+ processUtils);
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
+ kernelCpuThreadReader.getProcessCpuUsageByUids(uidPredicate);
+ processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.processId));
+
+ assertEquals(expectedUids.length, processCpuUsageByUids.size());
+ for (int i = 0; i < expectedUids.length; i++) {
+ KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+ processCpuUsageByUids.get(i);
+ 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}});
+ }
+ }
+
+ private void setupDirectory(Path processPath, int[] threadIds, String processName,
+ String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) throws IOException {
+ // Make /proc/$PID
+ assertTrue(processPath.toFile().mkdirs());
+
+ // Make /proc/$PID/task
+ final Path selfThreadsPath = processPath.resolve("task");
assertTrue(selfThreadsPath.toFile().mkdirs());
- // Make /proc/self/cmdline
- Files.write(selfPath.resolve("cmdline"), PROCESS_NAME.getBytes());
+ // Make /proc/$PID/cmdline
+ Files.write(processPath.resolve("cmdline"), processName.getBytes());
// Make thread directories in reverse order, as they are read in order of creation by
// CpuThreadProcReader
- for (int i = 0; i < THREAD_IDS.length; i++) {
- // Make /proc/self/task/$TID
- final Path threadPath = selfThreadsPath.resolve(String.valueOf(THREAD_IDS[i]));
+ for (int i = 0; i < threadIds.length; i++) {
+ // Make /proc/$PID/task/$TID
+ final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
assertTrue(threadPath.toFile().mkdirs());
- // Make /proc/self/task/$TID/comm
- Files.write(threadPath.resolve("comm"), THREAD_NAMES[i].getBytes());
+ // Make /proc/$PID/task/$TID/comm
+ Files.write(threadPath.resolve("comm"), threadNames[i].getBytes());
- // Make /proc/self/task/$TID/time_in_state
+ // Make /proc/$PID/task/$TID/time_in_state
final OutputStream timeInStateStream =
Files.newOutputStream(threadPath.resolve("time_in_state"));
- for (int j = 0; j < THREAD_CPU_FREQUENCIES.length; j++) {
- final String line = String.valueOf(THREAD_CPU_FREQUENCIES[j]) + " "
- + String.valueOf(THREAD_CPU_TIMES[i][j]) + "\n";
+ for (int j = 0; j < cpuFrequencies.length; j++) {
+ final String line = String.valueOf(cpuFrequencies[j]) + " "
+ + String.valueOf(cpuTimes[i][j]) + "\n";
timeInStateStream.write(line.getBytes());
}
timeInStateStream.close();
}
+ }
- final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
- mProcDirectory.toPath(),
- mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"));
- final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
- kernelCpuThreadReader.getCurrentProcessCpuUsage();
-
+ private void checkResults(KernelCpuThreadReader.ProcessCpuUsage processCpuUsage,
+ int[] readerCpuFrequencies, int uid, int processId, int[] threadIds, String processName,
+ String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) {
assertNotNull(processCpuUsage);
- assertEquals(android.os.Process.myPid(), processCpuUsage.processId);
- assertEquals(android.os.Process.myUid(), processCpuUsage.uid);
- assertEquals(PROCESS_NAME, processCpuUsage.processName);
+ assertEquals(processId, processCpuUsage.processId);
+ assertEquals(uid, processCpuUsage.uid);
+ assertEquals(processName, processCpuUsage.processName);
// Sort the thread CPU usages to compare with test case
final ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
@@ -124,21 +242,21 @@
int threadCount = 0;
for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage : threadCpuUsages) {
- assertEquals(THREAD_IDS[threadCount], threadCpuUsage.threadId);
- assertEquals(THREAD_NAMES[threadCount], threadCpuUsage.threadName);
+ assertEquals(threadIds[threadCount], threadCpuUsage.threadId);
+ assertEquals(threadNames[threadCount], threadCpuUsage.threadName);
for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) {
assertEquals(
- THREAD_CPU_TIMES[threadCount][i] * 10,
+ cpuTimes[threadCount][i] * 10,
threadCpuUsage.usageTimesMillis[i]);
assertEquals(
- THREAD_CPU_FREQUENCIES[i],
- kernelCpuThreadReader.getCpuFrequenciesKhz()[i]);
+ cpuFrequencies[i],
+ readerCpuFrequencies[i]);
}
threadCount++;
}
- assertEquals(threadCount, THREAD_IDS.length);
+ assertEquals(threadCount, threadIds.length);
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
new file mode 100644
index 0000000..adafda0
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseLongArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidActiveTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidActiveTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidActiveTimeReader mReader;
+ private VerifiableCallback mCallback;
+
+ private Random mRand = new Random(12345);
+ private final int mCpus = 4;
+ private final String mHeadline = "cpus: 4\n";
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidActiveTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[][] times = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], getActiveTime(times[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call will only return deltas.
+ mCallback.clear();
+ final long[][] newTimes1 = increaseTime(times);
+ writeToFile(mHeadline + uidLines(mUids, newTimes1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ mCallback.clear();
+ final long[][] newTimes2 = increaseTime(newTimes1);
+ writeToFile(mHeadline + uidLines(mUids, newTimes2));
+ mReader.readDelta(null);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ mCallback.clear();
+ final long[][] newTimes3 = increaseTime(newTimes2);
+ writeToFile(mHeadline + uidLines(mUids, newTimes3));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call should still return absolute values
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaDecreasedTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
+ times2[0][0] = 100;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testReadDeltaNegativeTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time is -ve.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] *= -1;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j] / 10);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000;
+ }
+ }
+ return newTime;
+ }
+
+ private long getActiveTime(long[] times) {
+ return times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<Long> {
+ SparseLongArray mData = new SparseLongArray();
+
+ public void verify(int uid, long time) {
+ assertEquals(time, mData.get(uid));
+ mData.delete(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, Long time) {
+ mData.put(uid, time);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
new file mode 100644
index 0000000..ad20d84
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -0,0 +1,278 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidClusterTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidClusterTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidClusterTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidClusterTimeReader mReader;
+ private VerifiableCallback mCallback;
+
+ private Random mRand = new Random(12345);
+ private final int mCpus = 6;
+ private final String mHeadline = "policy0: 4 policy4: 2\n";
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidClusterTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], clusterTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call will only return deltas.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(null);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ mCallback.clear();
+ final long[][] times4 = increaseTime(times3);
+ writeToFile(mHeadline + uidLines(mUids, times4));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times4[i]), clusterTime(times3[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], clusterTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call should still return absolute values
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], clusterTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaDecreasedTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
+ times2[0][0] = 100;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times3[i]), clusterTime(times2[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testReadDeltaNegativeTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] *= -1;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times3[i]), clusterTime(times2[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ private long[] clusterTime(long[] times) {
+ // Assumes 4 + 2 cores
+ return new long[]{times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4,
+ times[4] + times[5] / 2};
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j] / 10);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000;
+ }
+ }
+ return newTime;
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+ SparseArray<long[]> mData = new SparseArray<>();
+
+ public void verify(int uid, long[] cpuTimes) {
+ long[] array = mData.get(uid);
+ assertNotNull(array);
+ assertArrayEquals(cpuTimes, array);
+ mData.remove(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, long[] times) {
+ long[] array = new long[times.length];
+ System.arraycopy(times, 0, array, 0, array.length);
+ mData.put(uid, array);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
new file mode 100644
index 0000000..1d3a98a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -0,0 +1,359 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+
+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 java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidFreqTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidFreqTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidFreqTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidFreqTimeReader mReader;
+ private VerifiableCallback mCallback;
+ @Mock
+ private PowerProfile mPowerProfile;
+
+ private Random mRand = new Random(12345);
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testReadFreqs_perClusterTimesNotAvailable() throws Exception {
+ final long[][] freqs = {
+ {1, 12, 123, 1234},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345},
+ {1, 12, 123, 23, 2345, 234567}
+ };
+ final int[] numClusters = {2, 2, 3, 1};
+ final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
+ for (int i = 0; i < freqs.length; ++i) {
+ mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ setCpuClusterFreqs(numClusters[i], numFreqs[i]);
+ writeToFile(freqsLine(freqs[i]));
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
+ Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
+ assertFalse(errMsg, mReader.perClusterTimesAvailable());
+
+ // Verify that a second call won't read the proc file again
+ assertTrue(mTestFile.delete());
+ actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ assertFalse(errMsg, mReader.perClusterTimesAvailable());
+ }
+ }
+
+ @Test
+ public void testReadFreqs_perClusterTimesAvailable() throws Exception {
+ final long[][] freqs = {
+ {1, 12, 123, 1234},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345, 1234567}
+ };
+ final int[] numClusters = {1, 2, 3};
+ final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
+ for (int i = 0; i < freqs.length; ++i) {
+ mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ setCpuClusterFreqs(numClusters[i], numFreqs[i]);
+ writeToFile(freqsLine(freqs[i]));
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
+ Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
+ assertTrue(errMsg, mReader.perClusterTimesAvailable());
+
+ // Verify that a second call won't read the proc file again
+ assertTrue(mTestFile.delete());
+ actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ assertTrue(errMsg, mReader.perClusterTimesAvailable());
+ }
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[] freqs = {110, 123, 145, 167, 289, 997};
+ final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
+
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], times[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that readDelta also reads the frequencies if not already available.
+ assertTrue(mTestFile.delete());
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs, actualFreqs);
+
+ // Verify that a second call will only return deltas.
+ mCallback.clear();
+ final long[][] newTimes1 = increaseTime(times);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(newTimes1[i], times[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ mCallback.clear();
+ final long[][] newTimes2 = increaseTime(newTimes1);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes2));
+ mReader.readDelta(null);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ mCallback.clear();
+ final long[][] newTimes3 = increaseTime(newTimes2);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes3));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(newTimes3[i], newTimes2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[] freqs = {110, 123, 145, 167, 289, 997};
+ final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
+
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that readDelta also reads the frequencies if not already available.
+ assertTrue(mTestFile.delete());
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs, actualFreqs);
+
+ // Verify that a second call should still return absolute values
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times2[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaWrongData() throws Exception {
+ final long[] freqs = {110, 123, 145, 167, 289, 997};
+ final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
+
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] = 1000;
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(times3[0], times1[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times3[i], times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there is no callback if any value in the proc file is -ve.
+ mCallback.clear();
+ final long[][] times4 = increaseTime(times3);
+ times4[0][0] *= -1;
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times4));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the internal state was not modified when the proc file had -ve value.
+ mCallback.clear();
+ final long[][] times5 = increaseTime(times4);
+ times5[0] = increaseTime(times3)[0];
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times5));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(times5[0], times3[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times5[i], times4[i]));
+ }
+
+ assertTrue(mTestFile.delete());
+ }
+
+ private String freqsLine(long[] freqs) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("uid:");
+ for (int i = 0; i < freqs.length; ++i) {
+ sb.append(" " + freqs[i]);
+ }
+ return sb.append('\n').toString();
+ }
+
+ private void setCpuClusterFreqs(int numClusters, int... clusterFreqs) {
+ assertEquals(numClusters, clusterFreqs.length);
+ when(mPowerProfile.getNumCpuClusters()).thenReturn(numClusters);
+ for (int i = 0; i < numClusters; ++i) {
+ when(mPowerProfile.getNumSpeedStepsInCpuCluster(i)).thenReturn(clusterFreqs[i]);
+ }
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j] / 10);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 10 + 10;
+ }
+ }
+ return newTime;
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+ SparseArray<long[]> mData = new SparseArray<>();
+
+ public void verify(int uid, long[] cpuTimes) {
+ long[] array = mData.get(uid);
+ assertNotNull(array);
+ assertArrayEquals(cpuTimes, array);
+ mData.remove(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, long[] times) {
+ long[] array = new long[times.length];
+ System.arraycopy(times, 0, array, 0, array.length);
+ mData.put(uid, array);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
new file mode 100644
index 0000000..9b4512b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -0,0 +1,271 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidUserSysTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidUserSysTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidUserSysTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mReader;
+ private VerifiableCallback mCallback;
+
+ private Random mRand = new Random(12345);
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+ private final long[][] mInitialTimes = new long[][]{
+ {15334000, 310964000},
+ {537000, 114000},
+ {40000, 10000},
+ {170000, 57000},
+ {5377000, 867000},
+ {47000, 17000}
+ };
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidUserSysTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testThrottler() throws Exception {
+ mReader = new KernelCpuUidUserSysTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), true);
+ mReader.setThrottle(500);
+
+ writeToFile(uidLines(mUids, mInitialTimes));
+ mReader.readDelta(mCallback);
+ assertEquals(6, mCallback.mData.size());
+
+ long[][] times1 = increaseTime(mInitialTimes);
+ writeToFile(uidLines(mUids, times1));
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ assertEquals(0, mCallback.mData.size());
+
+ SystemClock.sleep(600);
+
+ long[][] times2 = increaseTime(times1);
+ writeToFile(uidLines(mUids, times2));
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ assertEquals(6, mCallback.mData.size());
+
+ long[][] times3 = increaseTime(times2);
+ writeToFile(uidLines(mUids, times3));
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ assertEquals(0, mCallback.mData.size());
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[][] times1 = mInitialTimes;
+ writeToFile(uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that a second call will only return deltas.
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ final long[][] times3 = increaseTime(times2);
+ writeToFile(uidLines(mUids, times3));
+ mReader.readDelta(null);
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ final long[][] times4 = increaseTime(times3);
+ writeToFile(uidLines(mUids, times4));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaWrongData() throws Exception {
+ final long[][] times1 = mInitialTimes;
+ writeToFile(uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] = 1000;
+ writeToFile(uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[][] times1 = mInitialTimes;
+ writeToFile(uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that a second call should still return absolute values
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times2[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+ assertTrue(mTestFile.delete());
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j]);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(1000) * 1000 + 1000;
+ }
+ }
+ return newTime;
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+ SparseArray<long[]> mData = new SparseArray<>();
+
+ public void verify(int uid, long[] cpuTimes) {
+ long[] array = mData.get(uid);
+ assertNotNull(array);
+ assertArrayEquals(cpuTimes, array);
+ mData.remove(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, long[] times) {
+ long[] array = new long[times.length];
+ System.arraycopy(times, 0, array, 0, array.length);
+ mData.put(uid, array);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
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 f637b7c..31dde5c 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -105,7 +105,6 @@
assertThat(entry.recordedDelayMessageCount).isEqualTo(1);
assertThat(entry.delayMillis).isEqualTo(30);
assertThat(entry.maxDelayMillis).isEqualTo(30);
-
}
@Test
@@ -429,6 +428,28 @@
assertThat(entries).hasSize(0);
}
+ @Test
+ public void testAddsDebugEntries() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ looperStats.setAddDebugEntries(true);
+
+ Message message = mHandlerFirst.obtainMessage(1000);
+ message.when = looperStats.getSystemUptimeMillis();
+ Object token = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token, message);
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(3);
+ LooperStats.ExportedEntry debugEntry1 = entries.get(1);
+ assertThat(debugEntry1.handlerClassName).isEqualTo("");
+ assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
+ assertThat(debugEntry1.maxDelayMillis).isEqualTo(looperStats.getStartTimeMillis());
+ LooperStats.ExportedEntry debugEntry2 = entries.get(2);
+ assertThat(debugEntry2.handlerClassName).isEqualTo("");
+ assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis");
+ assertThat(debugEntry2.maxDelayMillis).isAtLeast(looperStats.getStartTimeMillis());
+ }
+
private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
try {
r.run();
@@ -450,6 +471,7 @@
super(samplingInterval, sizeCap);
this.mSamplingInterval = samplingInterval;
this.setDeviceState(mDeviceState.getReadonlyClient());
+ this.setAddDebugEntries(false);
}
void tickRealtime(long micros) {
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
index f2a531f..2893066 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
@@ -70,6 +70,23 @@
}
@Test
+ public void testHeaderFormat() throws IOException {
+ final Path initialTimeInStateFile = mProcDirectory.toPath().resolve(
+ "initial-time-in-state");
+ Files.write(initialTimeInStateFile, "header1\n1 2\nheader2:\n3 4\n5 6\n7 8\n".getBytes());
+ final ProcTimeInStateReader reader = new ProcTimeInStateReader(initialTimeInStateFile);
+
+ assertArrayEquals(
+ "Reported frequencies are correct",
+ new long[]{1, 3, 5, 7},
+ reader.getFrequenciesKhz());
+ assertArrayEquals(
+ "Reported usage times are correct",
+ new long[]{20, 40, 60, 80},
+ reader.getUsageTimesMillis(initialTimeInStateFile));
+ }
+
+ @Test
public void testDifferentFile() throws IOException {
Path initialTimeInStateFile = mProcDirectory.toPath().resolve("initial-time-in-state");
Files.write(initialTimeInStateFile, "1 2\n3 4\n5 6\n7 8\n".getBytes());
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index a4c5ed2..141948f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -201,6 +201,24 @@
<new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</split-permission>
+ <!-- Apps holding either the legacy READ or WRITE permissions will inherit
+ the ability to <em>read</em> new typed permissions in the Q release; they
+ won't gain the ability to <em>write</em> that content. -->
+ <!-- STOPSHIP(b/112545973): change targetSdk to Q when SDK version finalised -->
+ <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+ targetSdk="10000">
+ <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+ <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+ <new-permission name="android.permission.READ_MEDIA_IMAGES" />
+ </split-permission>
+ <!-- STOPSHIP(b/112545973): change targetSdk to Q when SDK version finalised -->
+ <split-permission name="android.permission.WRITE_EXTERNAL_STORAGE"
+ targetSdk="10000">
+ <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+ <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+ <new-permission name="android.permission.READ_MEDIA_IMAGES" />
+ </split-permission>
+
<!-- This is a list of all the libraries available for application
code to link against. -->
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 009e042..e3b165c 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -1869,8 +1869,8 @@
}
float scale = (float) dstDensity / srcDensity;
- int scaledWidth = (int) (mWidth * scale + 0.5f);
- int scaledHeight = (int) (mHeight * scale + 0.5f);
+ int scaledWidth = Math.max((int) (mWidth * scale + 0.5f), 1);
+ int scaledHeight = Math.max((int) (mHeight * scale + 0.5f), 1);
this.setTargetSize(scaledWidth, scaledHeight);
return dstDensity;
}
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..acb46b3 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -652,6 +652,17 @@
private @Nullable FontStyle mStyle;
/**
+ * Returns the maximum capacity of custom fallback families.
+ *
+ * This includes the the first font family passed to the constructor.
+ *
+ * @return the maximum number of font families for the custom fallback
+ */
+ public static @IntRange(from = 1) int getMaxCustomFallbackCount() {
+ return MAX_CUSTOM_FALLBACK;
+ }
+
+ /**
* Constructs a builder with a font family.
*
* @param family a family object
@@ -706,8 +717,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;
}
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index fdd638a..dea2f45 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -108,11 +108,10 @@
* Scaled mask based on the view bounds.
*/
private final Path mMask;
+ private final Path mMaskScaleOnly;
private final Matrix mMaskMatrix;
private final Region mTransparentRegion;
- private Bitmap mMaskBitmap;
-
/**
* Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and
* background layer.
@@ -156,8 +155,8 @@
sMask = PathParser.createPathFromPathData(
Resources.getSystem().getString(R.string.config_icon_mask));
}
- mMask = PathParser.createPathFromPathData(
- Resources.getSystem().getString(R.string.config_icon_mask));
+ mMask = new Path(sMask);
+ mMaskScaleOnly = new Path(mMask);
mMaskMatrix = new Matrix();
mCanvas = new Canvas();
mTransparentRegion = new Region();
@@ -329,24 +328,19 @@
}
private void updateMaskBoundsInternal(Rect b) {
+ // reset everything that depends on the view bounds
mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
+ sMask.transform(mMaskMatrix, mMaskScaleOnly);
+
+ mMaskMatrix.postTranslate(b.left, b.top);
sMask.transform(mMaskMatrix, mMask);
- if (mMaskBitmap == null || mMaskBitmap.getWidth() != b.width() ||
- mMaskBitmap.getHeight() != b.height()) {
- mMaskBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ALPHA_8);
+ if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width()
+ || mLayersBitmap.getHeight() != b.height()) {
mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
}
- // mMaskBitmap bound [0, w] x [0, h]
- mCanvas.setBitmap(mMaskBitmap);
- mPaint.setShader(null);
- mCanvas.drawPath(mMask, mPaint);
- // mMask bound [left, top, right, bottom]
- mMaskMatrix.postTranslate(b.left, b.top);
- mMask.reset();
- sMask.transform(mMaskMatrix, mMask);
- // reset everything that depends on the view bounds
+ mPaint.setShader(null);
mTransparentRegion.setEmpty();
mLayersShader = null;
}
@@ -371,9 +365,11 @@
mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint.setShader(mLayersShader);
}
- if (mMaskBitmap != null) {
+ if (mMaskScaleOnly != null) {
Rect bounds = getBounds();
- canvas.drawBitmap(mMaskBitmap, bounds.left, bounds.top, mPaint);
+ canvas.translate(bounds.left, bounds.top);
+ canvas.drawPath(mMaskScaleOnly, mPaint);
+ canvas.translate(-bounds.left, -bounds.top);
}
}
@@ -549,7 +545,7 @@
final ChildDrawable[] layers = mLayerState.mChildren;
for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
- if (layers[i].mDrawable.isProjected()) {
+ if (layers[i].mDrawable != null && layers[i].mDrawable.isProjected()) {
return true;
}
}
@@ -674,7 +670,7 @@
@Override
public int getAlpha() {
- return PixelFormat.TRANSLUCENT;
+ return mPaint.getAlpha();
}
@Override
@@ -718,10 +714,7 @@
@Override
public int getOpacity() {
- if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
- return mLayerState.mOpacityOverride;
- }
- return mLayerState.getOpacity();
+ return PixelFormat.TRANSLUCENT;
}
@Override
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/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 7216a22..072fe73 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -282,8 +282,11 @@
* Returns {@code true} if the entry no longer exists.
*/
public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
- return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid) ||
- keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
+ int ret = keystore.delete2(Credentials.USER_PRIVATE_KEY + alias, uid);
+ if (ret == KeyStore.KEY_NOT_FOUND) {
+ return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
+ }
+ return ret == KeyStore.NO_ERROR;
}
/**
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 6d58d95..6e6ed30 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -267,16 +267,20 @@
}
}
- public boolean delete(String key, int uid) {
+ int delete2(String key, int uid) {
try {
- int ret = mBinder.del(key, uid);
- return (ret == NO_ERROR || ret == KEY_NOT_FOUND);
+ return mBinder.del(key, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
- return false;
+ return SYSTEM_ERROR;
}
}
+ public boolean delete(String key, int uid) {
+ int ret = delete2(key, uid);
+ return ret == NO_ERROR || ret == KEY_NOT_FOUND;
+ }
+
@UnsupportedAppUsage
public boolean delete(String key) {
return delete(key, UID_SELF);
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 1cb0d25..365be10 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -349,7 +349,7 @@
goto exit;
}
}
- ret = tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
+ ret = tables[1].createIdmap(tables[0], targetCrc, overlayCrc,
targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 76db18d..2fe98b0 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -26,7 +26,9 @@
#include <algorithm>
#include <limits>
+#include <map>
#include <memory>
+#include <set>
#include <type_traits>
#include <android-base/macros.h>
@@ -7033,170 +7035,210 @@
return NO_ERROR;
}
-struct IdmapTypeMap {
- ssize_t overlayTypeId;
- size_t entryOffset;
- Vector<uint32_t> entryMap;
+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 = mData[targetTypeId];
+ entries.insert(std::make_pair(targetResId, overlayResId));
+ }
+
+ 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;
};
-status_t ResTable::createIdmap(const ResTable& overlay,
+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(lastSeen == 0xffffffff || lastSeen < ei->first);
+ mEntryPadding[ei->first] = (lastSeen == 0xffffffff) ? 0 : ei->first - lastSeen - 1;
+ lastSeen = ei->first;
+ totalEntries += 1 + mEntryPadding[ei->first];
+ }
+ mNumberOfEntriesIncludingPadding[ti->first] = totalEntries;
+ }
+ }
+
+ const auto& 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
+ 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> 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> mNumberOfEntriesIncludingPadding;
+};
+
+status_t ResTable::createIdmap(const ResTable& targetResTable,
uint32_t targetCrc, uint32_t overlayCrc,
const char* targetPath, const char* overlayPath,
void** outData, size_t* outSize) const
{
- // see README for details on the format of map
- if (mPackageGroups.size() == 0) {
- ALOGW("idmap: target package has no package groups, cannot create idmap\n");
+ if (targetPath == NULL || overlayPath == NULL || outData == NULL || outSize == NULL) {
+ ALOGE("idmap: unexpected NULL parameter");
+ return UNKNOWN_ERROR;
+ }
+ if (strlen(targetPath) > 255) {
+ ALOGE("idmap: target path exceeds idmap file format limit of 255 chars");
+ return UNKNOWN_ERROR;
+ }
+ if (strlen(overlayPath) > 255) {
+ ALOGE("idmap: overlay path exceeds idmap file format limit of 255 chars");
+ return UNKNOWN_ERROR;
+ }
+ if (mPackageGroups.size() == 0 || mPackageGroups[0]->packages.size() == 0) {
+ ALOGE("idmap: invalid overlay package");
+ return UNKNOWN_ERROR;
+ }
+ if (targetResTable.mPackageGroups.size() == 0 ||
+ targetResTable.mPackageGroups[0]->packages.size() == 0) {
+ ALOGE("idmap: invalid target package");
return UNKNOWN_ERROR;
}
- if (mPackageGroups[0]->packages.size() == 0) {
- ALOGW("idmap: target package has no packages in its first package group, "
- "cannot create idmap\n");
- return UNKNOWN_ERROR;
- }
+ 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);
+ const String16 targetPackageName(tmpName);
- // The number of resources overlaid that were not explicitly marked overlayable.
+ const PackageGroup* packageGroup = mPackageGroups[0];
+
+ // the number of resources overlaid that were not explicitly marked overlayable
size_t forcedOverlayCount = 0u;
- KeyedVector<uint8_t, IdmapTypeMap> map;
-
- // overlaid packages are assumed to contain only one package group
- const PackageGroup* pg = mPackageGroups[0];
-
- // starting size is header
- *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES;
-
- // target package id and number of types in map
- *outSize += 2 * sizeof(uint16_t);
-
- // overlay packages are assumed to contain only one package group
- const ResTable_package* overlayPackageStruct = overlay.mPackageGroups[0]->packages[0]->package;
- char16_t tmpName[sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0])];
- strcpy16_dtoh(tmpName, overlayPackageStruct->name, sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0]));
- const String16 overlayPackage(tmpName);
-
- for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
- const TypeList& typeList = pg->types[typeIndex];
+ // find the resources that exist in both packages
+ 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()) {
continue;
}
-
const Type* typeConfigs = typeList[0];
- IdmapTypeMap typeMap;
- typeMap.overlayTypeId = -1;
- typeMap.entryOffset = 0;
-
for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
- uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex);
- resource_name resName;
- if (!this->getResourceName(resID, false, &resName)) {
- if (typeMap.entryMap.isEmpty()) {
- typeMap.entryOffset++;
- }
+ uint32_t overlay_resid = Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex);
+ resource_name current_res;
+ if (!getResourceName(overlay_resid, false, ¤t_res)) {
continue;
}
uint32_t typeSpecFlags = 0u;
- const String16 overlayType(resName.type, resName.typeLen);
- const String16 overlayName(resName.name, resName.nameLen);
- uint32_t overlayResID = overlay.identifierForName(overlayName.string(),
- overlayName.size(),
- overlayType.string(),
- overlayType.size(),
- overlayPackage.string(),
- overlayPackage.size(),
- &typeSpecFlags);
- if (overlayResID == 0) {
- // No such target resource was found.
- if (typeMap.entryMap.isEmpty()) {
- typeMap.entryOffset++;
- }
+ const uint32_t target_resid = targetResTable.identifierForName(
+ current_res.name,
+ current_res.nameLen,
+ current_res.type,
+ current_res.typeLen,
+ targetPackageName.string(),
+ targetPackageName.size(),
+ &typeSpecFlags);
+
+ if (target_resid == 0) {
continue;
}
- // Now that we know this is being overlaid, check if it can be, and emit a warning if
- // it can't.
if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
- forcedOverlayCount++;
+ ++forcedOverlayCount;
}
- if (typeMap.overlayTypeId == -1) {
- typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
- }
-
- if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
- ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
- " but entries should map to resources of type %02zx",
- resID, overlayResID, typeMap.overlayTypeId);
- return BAD_TYPE;
- }
-
- if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
- // pad with 0xffffffff's (indicating non-existing entries) before adding this entry
- size_t index = typeMap.entryMap.size();
- size_t numItems = entryIndex - (typeMap.entryOffset + index);
- if (typeMap.entryMap.insertAt(0xffffffff, index, numItems) < 0) {
- return NO_MEMORY;
- }
- }
- typeMap.entryMap.add(Res_GETENTRY(overlayResID));
- }
-
- if (!typeMap.entryMap.isEmpty()) {
- if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) {
- return NO_MEMORY;
- }
- *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t));
+ typeMapping->add(target_resid, overlay_resid);
}
}
- if (map.isEmpty()) {
- ALOGW("idmap: no resources in overlay package present in base package");
+ if (typeMapping->empty()) {
+ ALOGE("idmap: no matching resources");
return UNKNOWN_ERROR;
}
+ 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
+ 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.getNumberOfEntriesIncludingPadding(ti->first) *
+ sizeof(uint32_t); // entries
+ }
if ((*outData = malloc(*outSize)) == NULL) {
return NO_MEMORY;
}
- uint32_t* data = (uint32_t*)*outData;
- *data++ = htodl(IDMAP_MAGIC);
- *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION);
- *data++ = htodl(targetCrc);
- *data++ = htodl(overlayCrc);
- const char* paths[] = { targetPath, overlayPath };
- for (int j = 0; j < 2; ++j) {
- char* p = (char*)data;
- const char* path = paths[j];
- const size_t I = strlen(path);
- if (I > 255) {
- ALOGV("path exceeds expected 255 characters: %s\n", path);
- return UNKNOWN_ERROR;
- }
- for (size_t i = 0; i < 256; ++i) {
- *p++ = i < I ? path[i] : '\0';
- }
- data += 256 / sizeof(uint32_t);
- }
- const size_t mapSize = map.size();
- uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
- *typeData++ = htods(pg->id);
- *typeData++ = htods(mapSize);
- for (size_t i = 0; i < mapSize; ++i) {
- uint8_t targetTypeId = map.keyAt(i);
- const IdmapTypeMap& typeMap = map[i];
- *typeData++ = htods(targetTypeId + 1);
- *typeData++ = htods(typeMap.overlayTypeId);
- *typeData++ = htods(typeMap.entryMap.size());
- *typeData++ = htods(typeMap.entryOffset);
+ // write idmap header
+ uint32_t* data = reinterpret_cast<uint32_t*>(*outData);
+ *data++ = htodl(IDMAP_MAGIC); // write: magic
+ *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); // write: version
+ *data++ = htodl(targetCrc); // write: target crc
+ *data++ = htodl(overlayCrc); // write: overlay crc
- const size_t entryCount = typeMap.entryMap.size();
- uint32_t* entries = reinterpret_cast<uint32_t*>(typeData);
- for (size_t j = 0; j < entryCount; j++) {
- entries[j] = htodl(typeMap.entryMap[j]);
+ char* charData = reinterpret_cast<char*>(data);
+ size_t pathLen = strlen(targetPath);
+ for (size_t i = 0; i < 256; ++i) {
+ *charData++ = i < pathLen ? targetPath[i] : '\0'; // write: target path
+ }
+ pathLen = strlen(overlayPath);
+ for (size_t i = 0; i < 256; ++i) {
+ *charData++ = i < pathLen ? overlayPath[i] : '\0'; // write: overlay path
+ }
+ data += (2 * 256) / sizeof(uint32_t);
+
+ // write idmap data header
+ uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
+ *typeData++ = htods(targetPackageStruct->id); // write: target package id
+ *typeData++ =
+ htods(static_cast<uint16_t>(fixedTypeMapping.size())); // write: type count
+
+ // write idmap data
+ 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
+ *typeData++ = htods(entryCount); // write: entry count
+ *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.getPadding(ei->first);
+ for (size_t i = 0; i < padding; ++i) {
+ *entryData++ = htodl(0xffffffff); // write: padding
+ }
+ *entryData++ = htodl(Res_GETENTRY(ei->second)); // write: (overlay) entry
}
typeData += entryCount * 2;
}
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 59abad45..ad33fcf 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1990,7 +1990,7 @@
// Return value: on success: NO_ERROR; caller is responsible for free-ing
// outData (using free(3)). On failure, any status_t value other than
// NO_ERROR; the caller should not free outData.
- status_t createIdmap(const ResTable& overlay,
+ status_t createIdmap(const ResTable& targetResTable,
uint32_t targetCrc, uint32_t overlayCrc,
const char* targetPath, const char* overlayPath,
void** outData, size_t* outSize) const;
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 26d2896..10b83a7 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -40,7 +40,7 @@
ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size()));
char target_name[256] = "com.android.basic";
- ASSERT_EQ(NO_ERROR, target_table_.createIdmap(overlay_table, 0, 0, target_name, target_name,
+ ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name,
&data_, &data_size_));
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 17d2db71..4a5b61a 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -45,9 +45,6 @@
],
product_variables: {
- device_uses_hwc2: {
- cflags: ["-DUSE_HWC2"],
- },
eng: {
lto: {
never: true,
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/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..d0df200 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);
}
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e4f96e9..98600db 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;
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index f0a3a95..1b5cb60 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -27,6 +27,7 @@
namespace uirenderer {
#define SK_MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
+#define SK_MATRIX_STRING_V "[%.9f %.9f %.9f] [%.9f %.9f %.9f] [%.9f %.9f %.9f]"
#define SK_MATRIX_ARGS(m) \
(m)->get(0), (m)->get(1), (m)->get(2), (m)->get(3), (m)->get(4), (m)->get(5), (m)->get(6), \
(m)->get(7), (m)->get(8)
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/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 9c707bab..ba34384 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -27,9 +27,9 @@
#include <SkAnimatedImage.h>
#include <SkCanvasStateUtils.h>
#include <SkColorFilter.h>
-#include <SkColorSpaceXformCanvas.h>
#include <SkDeque.h>
#include <SkDrawable.h>
+#include <SkFont.h>
#include <SkGraphics.h>
#include <SkImage.h>
#include <SkImagePriv.h>
@@ -60,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() {}
@@ -81,7 +70,6 @@
if (mCanvas != skiaCanvas) {
mCanvas = skiaCanvas;
mCanvasOwned.reset();
- mCanvasWrapper.reset();
}
mSaveStack.reset(nullptr);
}
@@ -91,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);
@@ -547,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,
@@ -589,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);
}
@@ -673,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,
@@ -710,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) {
@@ -732,6 +676,7 @@
float y, float boundsLeft, float boundsTop, float boundsRight,
float boundsBottom, float totalAdvance) {
if (count <= 0 || paint.nothingToDraw()) return;
+ SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
SkPaint paintCopy(paint);
if (mPaintFilter) {
mPaintFilter->filter(&paintCopy);
@@ -748,7 +693,7 @@
SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, boundsRight + x, boundsBottom + y);
SkTextBlobBuilder builder;
- const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(paintCopy, count, &bounds);
+ const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(font, count, &bounds);
glyphFunc(buffer.glyphs, buffer.pos);
sk_sp<SkTextBlob> textBlob(builder.make());
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/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/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 13d2dae..9b408fb 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <utils/MathUtils.h>
#include "LayerDrawable.h"
#include "GrBackendSurface.h"
@@ -32,6 +33,24 @@
}
}
+// This is a less-strict matrix.isTranslate() that will still report being translate-only
+// on imperceptibly small scaleX & scaleY values.
+static bool isBasicallyTranslate(const SkMatrix& matrix) {
+ if (!matrix.isScaleTranslate()) return false;
+ return MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY());
+}
+
+static bool shouldFilter(const SkMatrix& matrix) {
+ if (!matrix.isScaleTranslate()) return true;
+
+ // We only care about meaningful scale here
+ bool noScale = MathUtils::isOne(matrix.getScaleX())
+ && MathUtils::isOne(matrix.getScaleY());
+ bool pixelAligned = SkScalarIsInt(matrix.getTranslateX())
+ && SkScalarIsInt(matrix.getTranslateY());
+ return !(noScale && pixelAligned);
+}
+
bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
const SkRect* srcRect, const SkRect* dstRect,
bool useLayerTransform) {
@@ -70,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();
@@ -101,7 +120,7 @@
// Integer translation is defined as when src rect and dst rect align fractionally.
// Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
// only for SrcOver blending and without color filter (readback uses Src blending).
- bool isIntegerTranslate = totalMatrix.isTranslate()
+ bool isIntegerTranslate = isBasicallyTranslate(totalMatrix)
&& SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
== SkScalarFraction(skiaSrcRect.fLeft)
&& SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
@@ -112,10 +131,7 @@
canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint,
SkCanvas::kFast_SrcRectConstraint);
} else {
- bool isIntegerTranslate = totalMatrix.isTranslate()
- && SkScalarIsInt(totalMatrix[SkMatrix::kMTransX])
- && SkScalarIsInt(totalMatrix[SkMatrix::kMTransY]);
- if (layer->getForceFilter() || !isIntegerTranslate) {
+ if (layer->getForceFilter() || shouldFilter(totalMatrix)) {
paint.setFilterQuality(kLow_SkFilterQuality);
}
canvas->drawImage(layerImage.get(), 0, 0, &paint);
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index ea14d11..d80cb6d 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
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..f5de1c8 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());
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..5bab0ef5 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);
};
/**
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp
index 4bff715..90f8912 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,7 +487,6 @@
auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState);
if (image.get()) {
uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix);
- dataSpace = mCurrentDataSpace;
}
return image;
}
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h
index db392a9..96afd82 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
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 66b9b85..8a1bc4d 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;
}
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/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 2c73940..0331581 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -461,7 +461,7 @@
ProjectionLayer(int* drawCounter)
: SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
, mDrawCounter(drawCounter) {}
- virtual sk_sp<SkImage> onNewImageSnapshot() override {
+ virtual sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override {
EXPECT_EQ(3, (*mDrawCounter)++);
EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
300 - SCROLL_Y),
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/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 65b4e26..d16b8be 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -247,7 +247,7 @@
SkCanvas* onNewCanvas() override { return new T(); }
sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
- sk_sp<SkImage> onNewImageSnapshot() override { return nullptr; }
+ sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override { return nullptr; }
T* canvas() { return static_cast<T*>(getCanvas()); }
void onCopyOnWrite(ContentChangeMode) override {}
void onWritePixels(const SkPixmap&, int x, int y) override {}
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 5475898..cc8d83f 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -34,6 +34,10 @@
return (value >= -NON_ZERO_EPSILON) && (value <= NON_ZERO_EPSILON);
}
+ inline static bool isOne(float value) {
+ return areEqual(value, 1.0f);
+ }
+
inline static bool isPositive(float value) { return value >= NON_ZERO_EPSILON; }
/**
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index c5a6ec5..f1d9397 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -28,10 +28,13 @@
"libgui",
"libui",
"libinput",
- "libinputflinger",
"libnativewindow",
],
+ header_libs: [
+ "libinputflinger_headers",
+ ],
+
include_dirs: ["frameworks/native/services"],
cflags: [
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index eb3469e..7f4e5a5 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -24,7 +24,7 @@
#include <ui/DisplayInfo.h>
#include <input/Input.h>
-#include <inputflinger/PointerControllerInterface.h>
+#include <PointerControllerInterface.h>
#include <utils/BitSet.h>
#include <utils/RefBase.h>
#include <utils/Looper.h>
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/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 7627cf6..180183e 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -26,7 +26,9 @@
oneway interface ILocationListener
{
void onLocationChanged(in Location location);
- void onStatusChanged(String provider, int status, in Bundle extras);
void onProviderEnabled(String provider);
void onProviderDisabled(String provider);
+
+ // --- deprecated ---
+ void onStatusChanged(String provider, int status, in Bundle extras);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index b5d835a..ff2fad4 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -99,9 +99,10 @@
void clearTestProviderLocation(String provider, String opPackageName);
void setTestProviderEnabled(String provider, boolean enabled, String opPackageName);
void clearTestProviderEnabled(String provider, String opPackageName);
+
+ // --- deprecated ---
void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime,
String opPackageName);
- void clearTestProviderStatus(String provider, String opPackageName);
boolean sendExtraCommand(String provider, String command, inout Bundle extras);
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 88904c8..aa9dddc 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -44,29 +44,12 @@
void onLocationChanged(Location location);
/**
- * Called when the provider status changes. This method is called when
- * a provider is unable to fetch a location or if the provider has recently
- * become available after a period of unavailability.
+ * This callback will never be invoked and providers can be considers as always in the
+ * {@link LocationProvider#AVAILABLE} state.
*
- * @param provider the name of the location provider associated with this
- * update.
- * @param status {@link LocationProvider#OUT_OF_SERVICE} if the
- * provider is out of service, and this is not expected to change in the
- * near future; {@link LocationProvider#TEMPORARILY_UNAVAILABLE} if
- * the provider is temporarily unavailable but is expected to be available
- * shortly; and {@link LocationProvider#AVAILABLE} if the
- * provider is currently available.
- * @param extras an optional Bundle which will contain provider specific
- * status variables.
- *
- * <p> A number of common key/value pairs for the extras Bundle are listed
- * below. Providers that use any of the keys on this list must
- * provide the corresponding value as described below.
- *
- * <ul>
- * <li> satellites - the number of satellites used to derive the fix
- * </ul>
+ * @deprecated This callback will never be invoked.
*/
+ @Deprecated
void onStatusChanged(String provider, int status, Bundle extras);
/**
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 02680ab..b66ceef 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -146,9 +146,14 @@
public static final String KEY_PROXIMITY_ENTERING = "entering";
/**
+ * This key is no longer in use.
+ *
* Key used for a Bundle extra holding an Integer status value
* when a status change is broadcast using a PendingIntent.
+ *
+ * @deprecated Status changes are deprecated and no longer broadcast.
*/
+ @Deprecated
public static final String KEY_STATUS_CHANGED = "status";
/**
@@ -1581,8 +1586,7 @@
}
/**
- * Sets mock status values for the given provider. These values will be used in place
- * of any actual values from the provider.
+ * This method has no effect as provider status has been deprecated and is no longer supported.
*
* @param provider the provider name
* @param status the mock status
@@ -1593,7 +1597,10 @@
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * @deprecated This method has no effect.
*/
+ @Deprecated
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
try {
mService.setTestProviderStatus(provider, status, extras, updateTime,
@@ -1604,21 +1611,19 @@
}
/**
- * Removes any mock status values associated with the given provider.
+ * This method has no effect as provider status has been deprecated and is no longer supported.
*
* @param provider the provider name
- *
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * @deprecated This method has no effect.
*/
+ @Deprecated
public void clearTestProviderStatus(String provider) {
- try {
- mService.clearTestProviderStatus(provider, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setTestProviderStatus(provider, LocationProvider.AVAILABLE, null, 0L);
}
// --- GPS-specific support ---
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index c4fd097..b69a9d7 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -34,8 +34,23 @@
* user-specified criteria.
*/
public class LocationProvider {
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
public static final int OUT_OF_SERVICE = 0;
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
public static final int TEMPORARILY_UNAVAILABLE = 1;
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
public static final int AVAILABLE = 2;
/**
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 1e69f16..d19559e 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -11,8 +11,8 @@
method public abstract void onDisable();
method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public abstract void onEnable();
- method public abstract int onGetStatus(android.os.Bundle);
- method public abstract long onGetStatusUpdateTime();
+ method public deprecated int onGetStatus(android.os.Bundle);
+ method public deprecated long onGetStatusUpdateTime();
method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
method public final void reportLocation(android.location.Location);
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 30655f5..d45a4ba 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,14 +16,11 @@
package com.android.location.provider;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-
import android.content.Context;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
+import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -36,6 +33,10 @@
import com.android.internal.location.ProviderRequest;
import com.android.internal.util.FastPrintWriter;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+
/**
* Base class for location providers implemented as unbundled services.
*
@@ -173,6 +174,8 @@
}
/**
+ * This method will no longer be invoked.
+ *
* Returns a information on the status of this provider.
* <p>{@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is
* out of service, and this is not expected to change in the near
@@ -183,10 +186,17 @@
*
* <p>If extras is non-null, additional status information may be
* added to it in the form of provider-specific key/value pairs.
+ *
+ * @deprecated This method will no longer be invoked.
*/
- public abstract int onGetStatus(Bundle extras);
+ @Deprecated
+ public int onGetStatus(Bundle extras) {
+ return LocationProvider.AVAILABLE;
+ }
/**
+ * This method will no longer be invoked.
+ *
* Returns the time at which the status was last updated. It is the
* responsibility of the provider to appropriately set this value using
* {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
@@ -195,8 +205,13 @@
* the same status again.
*
* @return time of last status update in millis since last reboot
+ *
+ * @deprecated This method will no longer be invoked.
*/
- public abstract long onGetStatusUpdateTime();
+ @Deprecated
+ public long onGetStatusUpdateTime() {
+ return 0;
+ }
/**
* Implements addditional location provider specific additional commands.
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index f1325ce..4f23cca 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -595,10 +595,10 @@
case CONTENT_TYPE_MUSIC:
case CONTENT_TYPE_SONIFICATION:
case CONTENT_TYPE_SPEECH:
- mContentType = contentType;
- break;
+ mContentType = contentType;
+ break;
default:
- mUsage = CONTENT_TYPE_UNKNOWN;
+ mContentType = CONTENT_TYPE_UNKNOWN;
}
return this;
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 976d380..ff1bdd4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1911,7 +1911,7 @@
* system failed to generate a new session, a condition in which audio playback or recording
* will subsequently fail as well.
*/
- public static int generateAudioSessionId() {
+ public int generateAudioSessionId() {
int session = AudioSystem.newAudioSessionId();
if (session > 0) {
return session;
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..c226d49 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:
@@ -3418,7 +3414,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,
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/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..01a0cb6 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);
@@ -3125,9 +3215,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 995ebb2..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
@@ -1135,6 +1135,10 @@
maxChannels = 6;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
maxChannels = 16;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) {
+ sampleRates = new int[] { 44100, 48000, 96000, 192000 };
+ bitRates = Range.create(16000, 2688000);
+ maxChannels = 24;
} else {
Log.w(TAG, "Unsupported mime " + mime);
mParent.mError |= ERROR_UNSUPPORTED;
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
deleted file mode 100644
index a633e5f..0000000
--- a/media/java/android/media/MediaController2.java
+++ /dev/null
@@ -1,863 +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.media;
-
-import static android.media.MediaPlayerBase.BUFFERING_STATE_UNKNOWN;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.media.MediaPlaylistAgent.RepeatMode;
-import android.media.MediaPlaylistAgent.ShuffleMode;
-import android.media.MediaSession2.CommandButton;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.ErrorCode;
-import android.media.session.MediaSessionManager;
-import android.media.update.ApiLoader;
-import android.media.update.MediaController2Provider;
-import android.media.update.MediaController2Provider.PlaybackInfoProvider;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- * Allows an app to interact with an active {@link MediaSession2} in any status. Media buttons and
- * other commands can be sent to the session.
- * <p>
- * When you're done, use {@link #close()} to clean up resources. This also helps session service
- * to be destroyed when there's no controller associated with it.
- * <p>
- * When controlling {@link MediaSession2}, the controller will be available immediately after
- * the creation.
- * <p>
- * A controller can be created through token from {@link MediaSessionManager} if you hold the
- * signature|privileged permission "android.permission.MEDIA_CONTENT_CONTROL" permission or are
- * an enabled notification listener or by getting a {@link SessionToken2} directly the
- * the session owner.
- * <p>
- * MediaController2 objects are thread-safe.
- * <p>
- * @see MediaSession2
- */
-public class MediaController2 implements AutoCloseable {
- /**
- * Interface for listening to change in activeness of the {@link MediaSession2}. It's
- * active if and only if it has set a player.
- */
- public abstract static class ControllerCallback {
- /**
- * Called when the controller is successfully connected to the session. The controller
- * becomes available afterwards.
- *
- * @param controller the controller for this event
- * @param allowedCommands commands that's allowed by the session.
- */
- public void onConnected(@NonNull MediaController2 controller,
- @NonNull SessionCommandGroup2 allowedCommands) { }
-
- /**
- * Called when the session refuses the controller or the controller is disconnected from
- * the session. The controller becomes unavailable afterwards and the callback wouldn't
- * be called.
- * <p>
- * It will be also called after the {@link #close()}, so you can put clean up code here.
- * You don't need to call {@link #close()} after this.
- *
- * @param controller the controller for this event
- * @param controller controller for this event
- */
- public void onDisconnected(@NonNull MediaController2 controller) { }
-
- /**
- * Called when the session set the custom layout through the
- * {@link MediaSession2#setCustomLayout(ControllerInfo, List)}.
- * <p>
- * Can be called before {@link #onConnected(MediaController2, SessionCommandGroup2)} is
- * called.
- *
- * @param controller the controller for this event
- * @param layout
- */
- public void onCustomLayoutChanged(@NonNull MediaController2 controller,
- @NonNull List<CommandButton> layout) { }
-
- /**
- * Called when the session has changed anything related with the {@link PlaybackInfo}.
- *
- * @param controller the controller for this event
- * @param info new playback info
- */
- public void onPlaybackInfoChanged(@NonNull MediaController2 controller,
- @NonNull PlaybackInfo info) { }
-
- /**
- * Called when the allowed commands are changed by session.
- *
- * @param controller the controller for this event
- * @param commands newly allowed commands
- */
- public void onAllowedCommandsChanged(@NonNull MediaController2 controller,
- @NonNull SessionCommandGroup2 commands) { }
-
- /**
- * Called when the session sent a custom command.
- *
- * @param controller the controller for this event
- * @param command
- * @param args
- * @param receiver
- */
- public void onCustomCommand(@NonNull MediaController2 controller,
- @NonNull SessionCommand2 command, @Nullable Bundle args,
- @Nullable ResultReceiver receiver) { }
-
- /**
- * Called when the player state is changed.
- *
- * @param controller the controller for this event
- * @param state
- */
- public void onPlayerStateChanged(@NonNull MediaController2 controller, int state) { }
-
- /**
- * Called when playback speed is changed.
- *
- * @param controller the controller for this event
- * @param speed speed
- */
- public void onPlaybackSpeedChanged(@NonNull MediaController2 controller,
- float speed) { }
-
- /**
- * Called to report buffering events for a data source.
- * <p>
- * Use {@link #getBufferedPosition()} for current buffering position.
- *
- * @param controller the controller for this event
- * @param item the media item for which buffering is happening.
- * @param state the new buffering state.
- */
- public void onBufferingStateChanged(@NonNull MediaController2 controller,
- @NonNull MediaItem2 item, @MediaPlayerBase.BuffState int state) { }
-
- /**
- * Called to indicate that seeking is completed.
- *
- * @param controller the controller for this event.
- * @param position the previous seeking request.
- */
- public void onSeekCompleted(@NonNull MediaController2 controller, long position) { }
-
- /**
- * Called when a error from
- *
- * @param controller the controller for this event
- * @param errorCode error code
- * @param extras extra information
- */
- public void onError(@NonNull MediaController2 controller, @ErrorCode int errorCode,
- @Nullable Bundle extras) { }
-
- /**
- * Called when the player's currently playing item is changed
- * <p>
- * When it's called, you should invalidate previous playback information and wait for later
- * callbacks.
- *
- * @param controller the controller for this event
- * @param item new item
- * @see #onBufferingStateChanged(MediaController2, MediaItem2, int)
- */
- // TODO(jaewan): Use this (b/74316764)
- public void onCurrentMediaItemChanged(@NonNull MediaController2 controller,
- @NonNull MediaItem2 item) { }
-
- /**
- * Called when a playlist is changed.
- *
- * @param controller the controller for this event
- * @param list new playlist
- * @param metadata new metadata
- */
- public void onPlaylistChanged(@NonNull MediaController2 controller,
- @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) { }
-
- /**
- * Called when a playlist metadata is changed.
- *
- * @param controller the controller for this event
- * @param metadata new metadata
- */
- public void onPlaylistMetadataChanged(@NonNull MediaController2 controller,
- @Nullable MediaMetadata2 metadata) { }
-
- /**
- * Called when the shuffle mode is changed.
- *
- * @param controller the controller for this event
- * @param shuffleMode repeat mode
- * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
- * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
- * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
- */
- public void onShuffleModeChanged(@NonNull MediaController2 controller,
- @MediaPlaylistAgent.ShuffleMode int shuffleMode) { }
-
- /**
- * Called when the repeat mode is changed.
- *
- * @param controller the controller for this event
- * @param repeatMode repeat mode
- * @see MediaPlaylistAgent#REPEAT_MODE_NONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ALL
- * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
- */
- public void onRepeatModeChanged(@NonNull MediaController2 controller,
- @MediaPlaylistAgent.RepeatMode int repeatMode) { }
- }
-
- /**
- * Holds information about the current playback and how audio is handled for
- * this session.
- */
- // The same as MediaController.PlaybackInfo
- public static final class PlaybackInfo {
- /**
- * The session uses remote playback.
- */
- public static final int PLAYBACK_TYPE_REMOTE = 2;
- /**
- * The session uses local playback.
- */
- public static final int PLAYBACK_TYPE_LOCAL = 1;
-
- private final PlaybackInfoProvider mProvider;
-
- /**
- * @hide
- */
- public PlaybackInfo(PlaybackInfoProvider provider) {
- mProvider = provider;
- }
-
- /**
- * @hide
- */
- public PlaybackInfoProvider getProvider() {
- return mProvider;
- }
-
- /**
- * Get the type of playback which affects volume handling. One of:
- * <ul>
- * <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
- * <li>{@link #PLAYBACK_TYPE_REMOTE}</li>
- * </ul>
- *
- * @return The type of playback this session is using.
- */
- public int getPlaybackType() {
- return mProvider.getPlaybackType_impl();
- }
-
- /**
- * Get the audio attributes for this session. The attributes will affect
- * volume handling for the session. When the volume type is
- * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
- * remote volume handler.
- *
- * @return The attributes for this session.
- */
- public AudioAttributes getAudioAttributes() {
- return mProvider.getAudioAttributes_impl();
- }
-
- /**
- * Get the type of volume control that can be used. One of:
- * <ul>
- * <li>{@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}</li>
- * <li>{@link VolumeProvider2#VOLUME_CONTROL_RELATIVE}</li>
- * <li>{@link VolumeProvider2#VOLUME_CONTROL_FIXED}</li>
- * </ul>
- *
- * @return The type of volume control that may be used with this session.
- */
- public int getControlType() {
- return mProvider.getControlType_impl();
- }
-
- /**
- * Get the maximum volume that may be set for this session.
- *
- * @return The maximum allowed volume where this session is playing.
- */
- public int getMaxVolume() {
- return mProvider.getMaxVolume_impl();
- }
-
- /**
- * Get the current volume for this session.
- *
- * @return The current volume where this session is playing.
- */
- public int getCurrentVolume() {
- return mProvider.getCurrentVolume_impl();
- }
- }
-
- private final MediaController2Provider mProvider;
-
- /**
- * Create a {@link MediaController2} from the {@link SessionToken2}.
- * This connects to the session and may wake up the service if it's not available.
- *
- * @param context Context
- * @param token token to connect to
- * @param executor executor to run callbacks on.
- * @param callback controller callback to receive changes in
- */
- public MediaController2(@NonNull Context context, @NonNull SessionToken2 token,
- @NonNull @CallbackExecutor Executor executor, @NonNull ControllerCallback callback) {
- super();
-
- mProvider = createProvider(context, token, executor, callback);
- // This also connects to the token.
- // Explicit connect() isn't added on purpose because retrying connect() is impossible with
- // session whose session binder is only valid while it's active.
- // prevent a controller from reusable after the
- // session is released and recreated.
- mProvider.initialize();
- }
-
- MediaController2Provider createProvider(@NonNull Context context,
- @NonNull SessionToken2 token, @NonNull Executor executor,
- @NonNull ControllerCallback callback) {
- return ApiLoader.getProvider().createMediaController2(
- context, this, token, executor, callback);
- }
-
- /**
- * Release this object, and disconnect from the session. After this, callbacks wouldn't be
- * received.
- */
- @Override
- public void close() {
- mProvider.close_impl();
- }
-
- /**
- * @hide
- */
- public MediaController2Provider getProvider() {
- return mProvider;
- }
-
- /**
- * @return token
- */
- public @NonNull SessionToken2 getSessionToken() {
- return mProvider.getSessionToken_impl();
- }
-
- /**
- * Returns whether this class is connected to active {@link MediaSession2} or not.
- */
- public boolean isConnected() {
- return mProvider.isConnected_impl();
- }
-
- public void play() {
- mProvider.play_impl();
- }
-
- public void pause() {
- mProvider.pause_impl();
- }
-
- public void stop() {
- mProvider.stop_impl();
- }
-
- /**
- * Request that the player prepare its playback. In other words, other sessions can continue
- * to play during the preparation of this session. This method can be used to speed up the
- * start of the playback. Once the preparation is done, the session will change its playback
- * state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards, {@link #play} can be called
- * to start playback.
- */
- public void prepare() {
- mProvider.prepare_impl();
- }
-
- /**
- * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
- */
- public void fastForward() {
- mProvider.fastForward_impl();
- }
-
- /**
- * Rewinds playback. If playback is already rewinding this may increase the rate.
- */
- public void rewind() {
- mProvider.rewind_impl();
- }
-
- /**
- * Move to a new location in the media stream.
- *
- * @param pos Position to move to, in milliseconds.
- */
- public void seekTo(long pos) {
- mProvider.seekTo_impl(pos);
- }
-
- /**
- * Revisit this API later.
- * @hide
- */
- public void skipForward() {
- // TODO(jaewan): (Post-P) Discuss this API later.
- // To match with KEYCODE_MEDIA_SKIP_FORWARD
- }
-
- /**
- * @hide
- */
- public void skipBackward() {
- // TODO(jaewan): (Post-P) Discuss this API later.
- // To match with KEYCODE_MEDIA_SKIP_BACKWARD
- }
-
- /**
- * Request that the player start playback for a specific media id.
- *
- * @param mediaId The id of the requested media.
- * @param extras Optional extras that can include extra information about the media item
- * to be played.
- */
- public void playFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
- mProvider.playFromMediaId_impl(mediaId, extras);
- }
-
- /**
- * Request that the player start playback for a specific search query.
- *
- * @param query The search query. Should not be an empty string.
- * @param extras Optional extras that can include extra information about the query.
- */
- public void playFromSearch(@NonNull String query, @Nullable Bundle extras) {
- mProvider.playFromSearch_impl(query, extras);
- }
-
- /**
- * Request that the player start playback for a specific {@link Uri}.
- *
- * @param uri The URI of the requested media.
- * @param extras Optional extras that can include extra information about the media item
- * to be played.
- */
- public void playFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
- mProvider.playFromUri_impl(uri, extras);
- }
-
- /**
- * Request that the player prepare playback for a specific media id. In other words, other
- * sessions can continue to play during the preparation of this session. This method can be
- * used to speed up the start of the playback. Once the preparation is done, the session
- * will change its playback state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards,
- * {@link #play} can be called to start playback. If the preparation is not needed,
- * {@link #playFromMediaId} can be directly called without this method.
- *
- * @param mediaId The id of the requested media.
- * @param extras Optional extras that can include extra information about the media item
- * to be prepared.
- */
- public void prepareFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
- mProvider.prepareFromMediaId_impl(mediaId, extras);
- }
-
- /**
- * Request that the player prepare playback for a specific search query.
- * In other words, other sessions can continue to play during the preparation of this session.
- * This method can be used to speed up the start of the playback.
- * Once the preparation is done, the session will change its playback state to
- * {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards,
- * {@link #play} can be called to start playback. If the preparation is not needed,
- * {@link #playFromSearch} can be directly called without this method.
- *
- * @param query The search query. Should not be an empty string.
- * @param extras Optional extras that can include extra information about the query.
- */
- public void prepareFromSearch(@NonNull String query, @Nullable Bundle extras) {
- mProvider.prepareFromSearch_impl(query, extras);
- }
-
- /**
- * Request that the player prepare playback for a specific {@link Uri}. In other words,
- * other sessions can continue to play during the preparation of this session. This method
- * can be used to speed up the start of the playback. Once the preparation is done, the
- * session will change its playback state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}.
- * Afterwards, {@link #play} can be called to start playback. If the preparation is not needed,
- * {@link #playFromUri} can be directly called without this method.
- *
- * @param uri The URI of the requested media.
- * @param extras Optional extras that can include extra information about the media item
- * to be prepared.
- */
- public void prepareFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
- mProvider.prepareFromUri_impl(uri, extras);
- }
-
- /**
- * Set the volume of the output this session is playing on. The command will be ignored if it
- * does not support {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
- * <p>
- * If the session is local playback, this changes the device's volume with the stream that
- * session's player is using. Flags will be specified for the {@link AudioManager}.
- * <p>
- * If the session is remote player (i.e. session has set volume provider), its volume provider
- * will receive this request instead.
- *
- * @see #getPlaybackInfo()
- * @param value The value to set it to, between 0 and the reported max.
- * @param flags flags from {@link AudioManager} to include with the volume request for local
- * playback
- */
- public void setVolumeTo(int value, int flags) {
- mProvider.setVolumeTo_impl(value, flags);
- }
-
- /**
- * Adjust the volume of the output this session is playing on. The direction
- * must be one of {@link AudioManager#ADJUST_LOWER},
- * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
- * The command will be ignored if the session does not support
- * {@link VolumeProvider2#VOLUME_CONTROL_RELATIVE} or
- * {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
- * <p>
- * If the session is local playback, this changes the device's volume with the stream that
- * session's player is using. Flags will be specified for the {@link AudioManager}.
- * <p>
- * If the session is remote player (i.e. session has set volume provider), its volume provider
- * will receive this request instead.
- *
- * @see #getPlaybackInfo()
- * @param direction The direction to adjust the volume in.
- * @param flags flags from {@link AudioManager} to include with the volume request for local
- * playback
- */
- public void adjustVolume(int direction, int flags) {
- mProvider.adjustVolume_impl(direction, flags);
- }
-
- /**
- * Get an intent for launching UI associated with this session if one exists.
- *
- * @return A {@link PendingIntent} to launch UI or null.
- */
- public @Nullable PendingIntent getSessionActivity() {
- return mProvider.getSessionActivity_impl();
- }
-
- /**
- * Get the lastly cached player state from
- * {@link ControllerCallback#onPlayerStateChanged(MediaController2, int)}.
- *
- * @return player state
- */
- public int getPlayerState() {
- return mProvider.getPlayerState_impl();
- }
-
- /**
- * Gets the current playback position.
- * <p>
- * This returns the calculated value of the position, based on the difference between the
- * update time and current time.
- *
- * @return position
- */
- public long getCurrentPosition() {
- return mProvider.getCurrentPosition_impl();
- }
-
- /**
- * Get the lastly cached playback speed from
- * {@link ControllerCallback#onPlaybackSpeedChanged(MediaController2, float)}.
- *
- * @return speed
- */
- public float getPlaybackSpeed() {
- return mProvider.getPlaybackSpeed_impl();
- }
-
- /**
- * Set the playback speed.
- */
- public void setPlaybackSpeed(float speed) {
- // TODO(jaewan): implement this (b/74093080)
- }
-
-
- /**
- * Gets the current buffering state of the player.
- * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
- * buffered.
- * @return the buffering state.
- */
- public @MediaPlayerBase.BuffState int getBufferingState() {
- // TODO(jaewan): Implement.
- return BUFFERING_STATE_UNKNOWN;
- }
-
- /**
- * Gets the lastly cached buffered position from the session when
- * {@link ControllerCallback#onBufferingStateChanged(MediaController2, MediaItem2, int)} is
- * called.
- *
- * @return buffering position in millis
- */
- public long getBufferedPosition() {
- return mProvider.getBufferedPosition_impl();
- }
-
- /**
- * Get the current playback info for this session.
- *
- * @return The current playback info or null.
- */
- public @Nullable PlaybackInfo getPlaybackInfo() {
- return mProvider.getPlaybackInfo_impl();
- }
-
- /**
- * Rate the media. This will cause the rating to be set for the current user.
- * The rating style must follow the user rating style from the session.
- * You can get the rating style from the session through the
- * {@link MediaMetadata#getRating(String)} with the key
- * {@link MediaMetadata#METADATA_KEY_USER_RATING}.
- * <p>
- * If the user rating was {@code null}, the media item does not accept setting user rating.
- *
- * @param mediaId The id of the media
- * @param rating The rating to set
- */
- public void setRating(@NonNull String mediaId, @NonNull Rating2 rating) {
- mProvider.setRating_impl(mediaId, rating);
- }
-
- /**
- * Send custom command to the session
- *
- * @param command custom command
- * @param args optional argument
- * @param cb optional result receiver
- */
- public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
- @Nullable ResultReceiver cb) {
- mProvider.sendCustomCommand_impl(command, args, cb);
- }
-
- /**
- * Returns the cached playlist from
- * {@link ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)}.
- * <p>
- * This list may differ with the list that was specified with
- * {@link #setPlaylist(List, MediaMetadata2)} depending on the session implementation. Use media
- * items returned here for other playlist APIs such as {@link #skipToPlaylistItem(MediaItem2)}.
- *
- * @return The playlist. Can be {@code null} if the controller doesn't have enough permission or
- * the session hasn't set any playlist.
- */
- public @Nullable List<MediaItem2> getPlaylist() {
- return mProvider.getPlaylist_impl();
- }
-
- /**
- * Sets the playlist.
- * <p>
- * Even when the playlist is successfully set, use the playlist returned from
- * {@link #getPlaylist()} for playlist APIs such as {@link #skipToPlaylistItem(MediaItem2)}.
- * Otherwise the session in the remote process can't distinguish between media items.
- *
- * @param list playlist
- * @param metadata metadata of the playlist
- * @see #getPlaylist()
- * @see ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)
- */
- public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
- mProvider.setPlaylist_impl(list, metadata);
- }
-
- /**
- * Updates the playlist metadata
- *
- * @param metadata metadata of the playlist
- */
- public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
- mProvider.updatePlaylistMetadata_impl(metadata);
- }
-
- /**
- * Gets the lastly cached playlist playlist metadata either from
- * {@link ControllerCallback#onPlaylistMetadataChanged(MediaController2, MediaMetadata2)} or
- * {@link ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)}.
- *
- * @return metadata metadata of the playlist, or null if none is set
- */
- public @Nullable MediaMetadata2 getPlaylistMetadata() {
- return mProvider.getPlaylistMetadata_impl();
- }
-
-
- /**
- * Adds the media item to the playlist at position index. Index equals or greater than
- * the current playlist size will add the item at the end of the playlist.
- * <p>
- * This will not change the currently playing media item.
- * If index is less than or equal to the current index of the playlist,
- * the current index of the playlist will be incremented correspondingly.
- *
- * @param index the index you want to add
- * @param item the media item you want to add
- */
- public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
- mProvider.addPlaylistItem_impl(index, item);
- }
-
- /**
- * Removes the media item at index in the playlist.
- *<p>
- * If the item is the currently playing item of the playlist, current playback
- * will be stopped and playback moves to next source in the list.
- *
- * @param item the media item you want to add
- */
- public void removePlaylistItem(@NonNull MediaItem2 item) {
- mProvider.removePlaylistItem_impl(item);
- }
-
- /**
- * Replace the media item at index in the playlist. This can be also used to update metadata of
- * an item.
- *
- * @param index the index of the item to replace
- * @param item the new item
- */
- public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
- mProvider.replacePlaylistItem_impl(index, item);
- }
-
- /**
- * Get the lastly cached current item from
- * {@link ControllerCallback#onCurrentMediaItemChanged(MediaController2, MediaItem2)}.
- *
- * @return index of the current item
- */
- public MediaItem2 getCurrentMediaItem() {
- return mProvider.getCurrentMediaItem_impl();
- }
-
- /**
- * Skips to the previous item in the playlist.
- * <p>
- * This calls {@link MediaSession2#skipToPreviousItem()} if the session allows.
- */
- public void skipToPreviousItem() {
- mProvider.skipToPreviousItem_impl();
- }
-
- /**
- * Skips to the next item in the playlist.
- * <p>
- * This calls {@link MediaSession2#skipToNextItem()} if the session allows.
- */
- public void skipToNextItem() {
- mProvider.skipToNextItem_impl();
- }
-
- /**
- * Skips to the item in the playlist.
- * <p>
- * This calls {@link MediaSession2#skipToPlaylistItem(MediaItem2)} if the session allows.
- *
- * @param item The item in the playlist you want to play
- */
- public void skipToPlaylistItem(@NonNull MediaItem2 item) {
- mProvider.skipToPlaylistItem_impl(item);
- }
-
- /**
- * Gets the cached repeat mode from the {@link ControllerCallback#onRepeatModeChanged(
- * MediaController2, int)}.
- *
- * @return repeat mode
- * @see MediaPlaylistAgent#REPEAT_MODE_NONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ALL
- * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
- */
- public @RepeatMode int getRepeatMode() {
- return mProvider.getRepeatMode_impl();
- }
-
- /**
- * Sets the repeat mode.
- *
- * @param repeatMode repeat mode
- * @see MediaPlaylistAgent#REPEAT_MODE_NONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ALL
- * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
- */
- public void setRepeatMode(@RepeatMode int repeatMode) {
- mProvider.setRepeatMode_impl(repeatMode);
- }
-
- /**
- * Gets the cached shuffle mode from the {@link ControllerCallback#onShuffleModeChanged(
- * MediaController2, int)}.
- *
- * @return The shuffle mode
- * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
- * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
- * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
- */
- public @ShuffleMode int getShuffleMode() {
- return mProvider.getShuffleMode_impl();
- }
-
- /**
- * Sets the shuffle mode.
- *
- * @param shuffleMode The shuffle mode
- * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
- * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
- * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
- */
- public void setShuffleMode(@ShuffleMode int shuffleMode) {
- mProvider.setShuffleMode_impl(shuffleMode);
- }
-}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index c203fa9..7785900 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -434,9 +434,12 @@
*/
@NonNull
public List<AudioPresentation> getAudioPresentations(int trackIndex) {
- return new ArrayList<AudioPresentation>();
+ return native_getAudioPresentations(trackIndex);
}
+ @NonNull
+ private native List<AudioPresentation> native_getAudioPresentations(int trackIndex);
+
/**
* Get the PSSH info if present.
* @return a map of uuid-to-bytes, with the uuid specifying
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 5dee16e..284e422 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -138,6 +138,7 @@
public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3";
public static final String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+ public static final String MIMETYPE_AUDIO_AC4 = "audio/ac4";
public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
/**
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
deleted file mode 100644
index 423a1fd..0000000
--- a/media/java/android/media/MediaItem2.java
+++ /dev/null
@@ -1,220 +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.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.media.update.ApiLoader;
-import android.media.update.MediaItem2Provider;
-import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * @hide
- * A class with information on a single media item with the metadata information.
- * Media item are application dependent so we cannot guarantee that they contain the right values.
- * <p>
- * When it's sent to a controller or browser, it's anonymized and data descriptor wouldn't be sent.
- * <p>
- * This object isn't a thread safe.
- */
-public class MediaItem2 {
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
- public @interface Flags { }
-
- /**
- * Flag: Indicates that the item has children of its own.
- */
- public static final int FLAG_BROWSABLE = 1 << 0;
-
- /**
- * Flag: Indicates that the item is playable.
- * <p>
- * The id of this item may be passed to
- * {@link MediaController2#playFromMediaId(String, Bundle)}
- */
- public static final int FLAG_PLAYABLE = 1 << 1;
-
- private final MediaItem2Provider mProvider;
-
- /**
- * Create a new media item
- * @hide
- */
- public MediaItem2(MediaItem2Provider provider) {
- mProvider = provider;
- }
-
- /**
- * @hide
- */
- public MediaItem2Provider getProvider() {
- return mProvider;
- }
-
- /**
- * Return this object as a bundle to share between processes.
- *
- * @return a new bundle instance
- */
- public Bundle toBundle() {
- return mProvider.toBundle_impl();
- }
-
- public static MediaItem2 fromBundle(Bundle bundle) {
- return ApiLoader.getProvider().fromBundle_MediaItem2(bundle);
- }
-
- public String toString() {
- return mProvider.toString_impl();
- }
-
- /**
- * Gets the flags of the item.
- */
- public @Flags int getFlags() {
- return mProvider.getFlags_impl();
- }
-
- /**
- * Returns whether this item is browsable.
- * @see #FLAG_BROWSABLE
- */
- public boolean isBrowsable() {
- return mProvider.isBrowsable_impl();
- }
-
- /**
- * Returns whether this item is playable.
- * @see #FLAG_PLAYABLE
- */
- public boolean isPlayable() {
- return mProvider.isPlayable_impl();
- }
-
- /**
- * Set a metadata. If the metadata is not null, its id should be matched with this instance's
- * media id.
- *
- * @param metadata metadata to update
- */
- public void setMetadata(@Nullable MediaMetadata2 metadata) {
- mProvider.setMetadata_impl(metadata);
- }
-
- /**
- * Returns the metadata of the media.
- */
- public @Nullable MediaMetadata2 getMetadata() {
- return mProvider.getMetadata_impl();
- }
-
- /**
- * Returns the media id for this item.
- */
- public @NonNull String getMediaId() {
- return mProvider.getMediaId_impl();
- }
-
- /**
- * Return the {@link DataSourceDesc}
- * <p>
- * Can be {@code null} if the MediaItem2 came from another process and anonymized
- *
- * @return data source descriptor
- */
- public @Nullable DataSourceDesc getDataSourceDesc() {
- return mProvider.getDataSourceDesc_impl();
- }
-
- @Override
- public boolean equals(Object obj) {
- return mProvider.equals_impl(obj);
- }
-
- /**
- * Build {@link MediaItem2}
- */
- public static final class Builder {
- private final MediaItem2Provider.BuilderProvider mProvider;
-
- /**
- * Constructor for {@link Builder}
- *
- * @param flags
- */
- public Builder(@Flags int flags) {
- mProvider = ApiLoader.getProvider().createMediaItem2Builder(this, flags);
- }
-
- /**
- * Set the media id of this instance. {@code null} for unset.
- * <p>
- * Media id is used to identify a media contents between session and controller.
- * <p>
- * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
- * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
- * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
- * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
- *
- * @param mediaId media id
- * @return this instance for chaining
- */
- public Builder setMediaId(@Nullable String mediaId) {
- return mProvider.setMediaId_impl(mediaId);
- }
-
- /**
- * Set the metadata of this instance. {@code null} for unset.
- * <p>
- * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
- * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
- * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
- * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
- *
- * @param metadata metadata
- * @return this instance for chaining
- */
- public Builder setMetadata(@Nullable MediaMetadata2 metadata) {
- return mProvider.setMetadata_impl(metadata);
- }
-
- /**
- * Set the data source descriptor for this instance. {@code null} for unset.
- *
- * @param dataSourceDesc data source descriptor
- * @return this instance for chaining
- */
- public Builder setDataSourceDesc(@Nullable DataSourceDesc dataSourceDesc) {
- return mProvider.setDataSourceDesc_impl(dataSourceDesc);
- }
-
- /**
- * Build {@link MediaItem2}.
- *
- * @return a new {@link MediaItem2}.
- */
- public MediaItem2 build() {
- return mProvider.build_impl();
- }
- }
-}
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
deleted file mode 100644
index 7b03ae0..0000000
--- a/media/java/android/media/MediaMetadata2.java
+++ /dev/null
@@ -1,854 +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.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-import android.graphics.Bitmap;
-import android.media.update.ApiLoader;
-import android.media.update.MediaMetadata2Provider;
-import android.net.Uri;
-import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Set;
-
-/**
- * @hide
- * Contains metadata about an item, such as the title, artist, etc.
- */
-// New version of MediaMetadata with following changes
-// - Don't implement Parcelable for updatable support.
-// - Also support MediaDescription features. MediaDescription is deprecated instead because
-// it was insufficient for controller to display media contents.
-public final class MediaMetadata2 {
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the title of the media.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the artist of the media.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
-
- /**
- * The metadata key for a {@link Long} typed value to retrieve the information about the
- * duration of the media in ms. A negative duration indicates that the duration is unknown
- * (or infinite).
- *
- * @see Builder#putLong(String, long)
- * @see #getLong(String)
- */
- public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the album title for the media.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the author of the media.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the writer of the media.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the composer of the media.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the compilation status of the media.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the date the media was created or published.
- * The format is unspecified but RFC 3339 is recommended.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
-
- /**
- * The metadata key for a {@link Long} typed value to retrieve the information about the year
- * the media was created or published.
- *
- * @see Builder#putLong(String, long)
- * @see #getLong(String)
- */
- public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the genre of the media.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
-
- /**
- * The metadata key for a {@link Long} typed value to retrieve the information about the
- * track number for the media.
- *
- * @see Builder#putLong(String, long)
- * @see #getLong(String)
- */
- public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
-
- /**
- * The metadata key for a {@link Long} typed value to retrieve the information about the
- * number of tracks in the media's original source.
- *
- * @see Builder#putLong(String, long)
- * @see #getLong(String)
- */
- public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
-
- /**
- * The metadata key for a {@link Long} typed value to retrieve the information about the
- * disc number for the media's original source.
- *
- * @see Builder#putLong(String, long)
- * @see #getLong(String)
- */
- public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the artist for the album of the media's original source.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
-
- /**
- * The metadata key for a {@link Bitmap} typed value to retrieve the information about the
- * artwork for the media.
- * The artwork should be relatively small and may be scaled down if it is too large.
- * For higher resolution artwork, {@link #METADATA_KEY_ART_URI} should be used instead.
- *
- * @see Builder#putBitmap(String, Bitmap)
- * @see #getBitmap(String)
- */
- public static final String METADATA_KEY_ART = "android.media.metadata.ART";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about Uri of the artwork for the media.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
-
- /**
- * The metadata key for a {@link Bitmap} typed value to retrieve the information about the
- * artwork for the album of the media's original source.
- * The artwork should be relatively small and may be scaled down if it is too large.
- * For higher resolution artwork, {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
- *
- * @see Builder#putBitmap(String, Bitmap)
- * @see #getBitmap(String)
- */
- public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the Uri of the artwork for the album of the media's original source.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
-
- /**
- * The metadata key for a {@link Rating2} typed value to retrieve the information about the
- * user's rating for the media.
- *
- * @see Builder#putRating(String, Rating2)
- * @see #getRating(String)
- */
- public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
-
- /**
- * The metadata key for a {@link Rating2} typed value to retrieve the information about the
- * overall rating for the media.
- *
- * @see Builder#putRating(String, Rating2)
- * @see #getRating(String)
- */
- public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the title that is suitable for display to the user.
- * It will generally be the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
- * When displaying media described by this metadata, this should be preferred if present.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the subtitle that is suitable for display to the user.
- * When displaying a second line for media described by this metadata, this should be preferred
- * to other fields if present.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_DISPLAY_SUBTITLE
- = "android.media.metadata.DISPLAY_SUBTITLE";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the description that is suitable for display to the user.
- * When displaying more information for media described by this metadata,
- * this should be preferred to other fields if present.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_DISPLAY_DESCRIPTION
- = "android.media.metadata.DISPLAY_DESCRIPTION";
-
- /**
- * The metadata key for a {@link Bitmap} typed value to retrieve the information about the icon
- * or thumbnail that is suitable for display to the user.
- * When displaying an icon for media described by this metadata, this should be preferred to
- * other fields if present.
- * <p>
- * The icon should be relatively small and may be scaled down if it is too large.
- * For higher resolution artwork, {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
- *
- * @see Builder#putBitmap(String, Bitmap)
- * @see #getBitmap(String)
- */
- public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the Uri of icon or thumbnail that is suitable for display to the user.
- * When displaying more information for media described by this metadata, the
- * display description should be preferred to other fields when present.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_DISPLAY_ICON_URI
- = "android.media.metadata.DISPLAY_ICON_URI";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the media ID of the content. This value is specific to the
- * service providing the content. If used, this should be a persistent
- * unique key for the underlying content. It may be used with
- * {@link MediaController2#playFromMediaId(String, Bundle)}
- * to initiate playback when provided by a {@link MediaBrowser2} connected to
- * the same app.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
-
- /**
- * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
- * information about the Uri of the content. This value is specific to the service providing the
- * content. It may be used with {@link MediaController2#playFromUri(Uri, Bundle)}
- * to initiate playback when provided by a {@link MediaBrowser2} connected to the same app.
- *
- * @see Builder#putText(String, CharSequence)
- * @see Builder#putString(String, String)
- * @see #getText(String)
- * @see #getString(String)
- */
- public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
-
- /**
- * The metadata key for a {@link Long} typed value to retrieve the information about the
- * bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth
- * AVRCP 1.5. It should be one of the following:
- * <ul>
- * <li>{@link #BT_FOLDER_TYPE_MIXED}</li>
- * <li>{@link #BT_FOLDER_TYPE_TITLES}</li>
- * <li>{@link #BT_FOLDER_TYPE_ALBUMS}</li>
- * <li>{@link #BT_FOLDER_TYPE_ARTISTS}</li>
- * <li>{@link #BT_FOLDER_TYPE_GENRES}</li>
- * <li>{@link #BT_FOLDER_TYPE_PLAYLISTS}</li>
- * <li>{@link #BT_FOLDER_TYPE_YEARS}</li>
- * </ul>
- *
- * @see Builder#putLong(String, long)
- * @see #getLong(String)
- */
- public static final String METADATA_KEY_BT_FOLDER_TYPE
- = "android.media.metadata.BT_FOLDER_TYPE";
-
- /**
- * The type of folder that is unknown or contains media elements of mixed types as specified in
- * the section 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_MIXED = 0;
-
- /**
- * The type of folder that contains media elements only as specified in the section 6.10.2.2 of
- * the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_TITLES = 1;
-
- /**
- * The type of folder that contains folders categorized by album as specified in the section
- * 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_ALBUMS = 2;
-
- /**
- * The type of folder that contains folders categorized by artist as specified in the section
- * 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_ARTISTS = 3;
-
- /**
- * The type of folder that contains folders categorized by genre as specified in the section
- * 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_GENRES = 4;
-
- /**
- * The type of folder that contains folders categorized by playlist as specified in the section
- * 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_PLAYLISTS = 5;
-
- /**
- * The type of folder that contains folders categorized by year as specified in the section
- * 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_YEARS = 6;
-
- /**
- * The metadata key for a {@link Long} typed value to retrieve the information about whether
- * the media is an advertisement. A value of 0 indicates it is not an advertisement.
- * A value of 1 or non-zero indicates it is an advertisement.
- * If not specified, this value is set to 0 by default.
- *
- * @see Builder#putLong(String, long)
- * @see #getLong(String)
- */
- public static final String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
-
- /**
- * The metadata key for a {@link Long} typed value to retrieve the information about the
- * download status of the media which will be used for later offline playback. It should be
- * one of the following:
- *
- * <ul>
- * <li>{@link #STATUS_NOT_DOWNLOADED}</li>
- * <li>{@link #STATUS_DOWNLOADING}</li>
- * <li>{@link #STATUS_DOWNLOADED}</li>
- * </ul>
- *
- * @see Builder#putLong(String, long)
- * @see #getLong(String)
- */
- public static final String METADATA_KEY_DOWNLOAD_STATUS =
- "android.media.metadata.DOWNLOAD_STATUS";
-
- /**
- * The status value to indicate the media item is not downloaded.
- *
- * @see #METADATA_KEY_DOWNLOAD_STATUS
- */
- public static final long STATUS_NOT_DOWNLOADED = 0;
-
- /**
- * The status value to indicate the media item is being downloaded.
- *
- * @see #METADATA_KEY_DOWNLOAD_STATUS
- */
- public static final long STATUS_DOWNLOADING = 1;
-
- /**
- * The status value to indicate the media item is downloaded for later offline playback.
- *
- * @see #METADATA_KEY_DOWNLOAD_STATUS
- */
- public static final long STATUS_DOWNLOADED = 2;
-
- /**
- * A {@link Bundle} extra.
- */
- public static final String METADATA_KEY_EXTRAS = "android.media.metadata.EXTRAS";
-
- /**
- * @hide
- */
- @StringDef({METADATA_KEY_TITLE, METADATA_KEY_ARTIST, METADATA_KEY_ALBUM, METADATA_KEY_AUTHOR,
- METADATA_KEY_WRITER, METADATA_KEY_COMPOSER, METADATA_KEY_COMPILATION,
- METADATA_KEY_DATE, METADATA_KEY_GENRE, METADATA_KEY_ALBUM_ARTIST, METADATA_KEY_ART_URI,
- METADATA_KEY_ALBUM_ART_URI, METADATA_KEY_DISPLAY_TITLE, METADATA_KEY_DISPLAY_SUBTITLE,
- METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_KEY_DISPLAY_ICON_URI,
- METADATA_KEY_MEDIA_ID, METADATA_KEY_MEDIA_URI})
- @Retention(RetentionPolicy.SOURCE)
- public @interface TextKey {}
-
- /**
- * @hide
- */
- @StringDef({METADATA_KEY_DURATION, METADATA_KEY_YEAR, METADATA_KEY_TRACK_NUMBER,
- METADATA_KEY_NUM_TRACKS, METADATA_KEY_DISC_NUMBER, METADATA_KEY_BT_FOLDER_TYPE,
- METADATA_KEY_ADVERTISEMENT, METADATA_KEY_DOWNLOAD_STATUS})
- @Retention(RetentionPolicy.SOURCE)
- public @interface LongKey {}
-
- /**
- * @hide
- */
- @StringDef({METADATA_KEY_ART, METADATA_KEY_ALBUM_ART, METADATA_KEY_DISPLAY_ICON})
- @Retention(RetentionPolicy.SOURCE)
- public @interface BitmapKey {}
-
- /**
- * @hide
- */
- @StringDef({METADATA_KEY_USER_RATING, METADATA_KEY_RATING})
- @Retention(RetentionPolicy.SOURCE)
- public @interface RatingKey {}
-
- /**
- * @hide
- */
- // TODO(jaewan): Add predefined float key.
- @Retention(RetentionPolicy.SOURCE)
- public @interface FloatKey {}
-
- private final MediaMetadata2Provider mProvider;
-
- /**
- * @hide
- */
- public MediaMetadata2(MediaMetadata2Provider provider) {
- mProvider = provider;
- }
-
- /**
- * Returns true if the given key is contained in the metadata
- *
- * @param key a String key
- * @return true if the key exists in this metadata, false otherwise
- */
- public boolean containsKey(@NonNull String key) {
- return mProvider.containsKey_impl(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if no mapping of
- * the desired type exists for the given key or a null value is explicitly
- * associated with the key.
- *
- * @param key The key the value is stored under
- * @return a CharSequence value, or null
- */
- public @Nullable CharSequence getText(@NonNull @TextKey String key) {
- return mProvider.getText_impl(key);
- }
-
- /**
- * Returns the media id, or {@code null} if the id doesn't exist.
- *<p>
- * This is equivalent to the {@link #getString(String)} with the {@link #METADATA_KEY_MEDIA_ID}.
- *
- * @return media id. Can be {@code null}
- * @see #METADATA_KEY_MEDIA_ID
- */
- public @Nullable String getMediaId() {
- return mProvider.getMediaId_impl();
- }
-
- /**
- * Returns the value associated with the given key, or null if no mapping of
- * the desired type exists for the given key or a null value is explicitly
- * associated with the key.
- *
- * @param key The key the value is stored under
- * @return a String value, or null
- */
- public @Nullable String getString(@NonNull @TextKey String key) {
- return mProvider.getString_impl(key);
- }
-
- /**
- * Returns the value associated with the given key, or 0L if no long exists
- * for the given key.
- *
- * @param key The key the value is stored under
- * @return a long value
- */
- public long getLong(@NonNull @LongKey String key) {
- return mProvider.getLong_impl(key);
- }
-
- /**
- * Return a {@link Rating2} for the given key or null if no rating exists for
- * the given key.
- * <p>
- * For the {@link #METADATA_KEY_USER_RATING}, A {@code null} return value means that user rating
- * cannot be set by {@link MediaController2}.
- *
- * @param key The key the value is stored under
- * @return A {@link Rating2} or {@code null}
- */
- public @Nullable Rating2 getRating(@NonNull @RatingKey String key) {
- return mProvider.getRating_impl(key);
- }
-
- /**
- * Return a {@link Bitmap} for the given key or null if no bitmap exists for
- * the given key.
- *
- * @param key The key the value is stored under
- * @return A {@link Bitmap} or null
- */
- public @Nullable Bitmap getBitmap(@NonNull @BitmapKey String key) {
- return mProvider.getBitmap_impl(key);
- }
-
- /**
- * Return the value associated with the given key, or 0.0f if no long exists
- * for the given key.
- *
- * @param key The key the value is stored under
- * @return a float value
- */
- public float getFloat(@NonNull @FloatKey String key) {
- return mProvider.getFloat_impl(key);
- }
-
- /**
- * Get the extra {@link Bundle} from the metadata object.
- *
- * @return A {@link Bundle} or {@code null}
- */
- public @Nullable Bundle getExtras() {
- return mProvider.getExtras_impl();
- }
-
- /**
- * Get the number of fields in this metadata.
- *
- * @return The number of fields in the metadata.
- */
- public int size() {
- return mProvider.size_impl();
- }
-
- /**
- * Returns a Set containing the Strings used as keys in this metadata.
- *
- * @return a Set of String keys
- */
- public @NonNull Set<String> keySet() {
- return mProvider.keySet_impl();
- }
-
- /**
- * Gets the bundle backing the metadata object. This is available to support
- * backwards compatibility. Apps should not modify the bundle directly.
- *
- * @return The Bundle backing this metadata.
- */
- public @NonNull Bundle toBundle() {
- return mProvider.toBundle_impl();
- }
-
- /**
- * Creates the {@link MediaMetadata2} from the bundle that previously returned by
- * {@link #toBundle()}.
- *
- * @param bundle bundle for the metadata
- * @return a new MediaMetadata2
- */
- public static @NonNull MediaMetadata2 fromBundle(@Nullable Bundle bundle) {
- return ApiLoader.getProvider().fromBundle_MediaMetadata2(bundle);
- }
-
- /**
- * Use to build MediaMetadata2 objects. The system defined metadata keys must
- * use the appropriate data type.
- */
- public static final class Builder {
- private final MediaMetadata2Provider.BuilderProvider mProvider;
-
- /**
- * Create an empty Builder. Any field that should be included in the
- * {@link MediaMetadata2} must be added.
- */
- public Builder() {
- mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(this);
- }
-
- /**
- * Create a Builder using a {@link MediaMetadata2} instance to set the
- * initial values. All fields in the source metadata will be included in
- * the new metadata. Fields can be overwritten by adding the same key.
- *
- * @param source
- */
- public Builder(@NonNull MediaMetadata2 source) {
- mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(this, source);
- }
-
- /**
- * @hide
- */
- public Builder(@NonNull MediaMetadata2Provider.BuilderProvider provider) {
- mProvider = provider;
- }
-
- /**
- * Put a CharSequence value into the metadata. Custom keys may be used,
- * but if the METADATA_KEYs defined in this class are used they may only
- * be one of the following:
- * <ul>
- * <li>{@link #METADATA_KEY_TITLE}</li>
- * <li>{@link #METADATA_KEY_ARTIST}</li>
- * <li>{@link #METADATA_KEY_ALBUM}</li>
- * <li>{@link #METADATA_KEY_AUTHOR}</li>
- * <li>{@link #METADATA_KEY_WRITER}</li>
- * <li>{@link #METADATA_KEY_COMPOSER}</li>
- * <li>{@link #METADATA_KEY_DATE}</li>
- * <li>{@link #METADATA_KEY_GENRE}</li>
- * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
- * <li>{@link #METADATA_KEY_ART_URI}</li>
- * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
- * </ul>
- *
- * @param key The key for referencing this value
- * @param value The CharSequence value to store
- * @return The Builder to allow chaining
- */
- public @NonNull Builder putText(@NonNull @TextKey String key,
- @Nullable CharSequence value) {
- return mProvider.putText_impl(key, value);
- }
-
- /**
- * Put a String value into the metadata. Custom keys may be used, but if
- * the METADATA_KEYs defined in this class are used they may only be one
- * of the following:
- * <ul>
- * <li>{@link #METADATA_KEY_TITLE}</li>
- * <li>{@link #METADATA_KEY_ARTIST}</li>
- * <li>{@link #METADATA_KEY_ALBUM}</li>
- * <li>{@link #METADATA_KEY_AUTHOR}</li>
- * <li>{@link #METADATA_KEY_WRITER}</li>
- * <li>{@link #METADATA_KEY_COMPOSER}</li>
- * <li>{@link #METADATA_KEY_DATE}</li>
- * <li>{@link #METADATA_KEY_GENRE}</li>
- * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
- * <li>{@link #METADATA_KEY_ART_URI}</li>
- * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
- * </ul>
- *
- * @param key The key for referencing this value
- * @param value The String value to store
- * @return The Builder to allow chaining
- */
- public @NonNull Builder putString(@NonNull @TextKey String key,
- @Nullable String value) {
- return mProvider.putString_impl(key, value);
- }
-
- /**
- * Put a long value into the metadata. Custom keys may be used, but if
- * the METADATA_KEYs defined in this class are used they may only be one
- * of the following:
- * <ul>
- * <li>{@link #METADATA_KEY_DURATION}</li>
- * <li>{@link #METADATA_KEY_TRACK_NUMBER}</li>
- * <li>{@link #METADATA_KEY_NUM_TRACKS}</li>
- * <li>{@link #METADATA_KEY_DISC_NUMBER}</li>
- * <li>{@link #METADATA_KEY_YEAR}</li>
- * <li>{@link #METADATA_KEY_BT_FOLDER_TYPE}</li>
- * <li>{@link #METADATA_KEY_ADVERTISEMENT}</li>
- * <li>{@link #METADATA_KEY_DOWNLOAD_STATUS}</li>
- * </ul>
- *
- * @param key The key for referencing this value
- * @param value The String value to store
- * @return The Builder to allow chaining
- */
- public @NonNull Builder putLong(@NonNull @LongKey String key, long value) {
- return mProvider.putLong_impl(key, value);
- }
-
- /**
- * Put a {@link Rating2} into the metadata. Custom keys may be used, but
- * if the METADATA_KEYs defined in this class are used they may only be
- * one of the following:
- * <ul>
- * <li>{@link #METADATA_KEY_RATING}</li>
- * <li>{@link #METADATA_KEY_USER_RATING}</li>
- * </ul>
- *
- * @param key The key for referencing this value
- * @param value The String value to store
- * @return The Builder to allow chaining
- */
- public @NonNull Builder putRating(@NonNull @RatingKey String key, @Nullable Rating2 value) {
- return mProvider.putRating_impl(key, value);
- }
-
- /**
- * Put a {@link Bitmap} into the metadata. Custom keys may be used, but
- * if the METADATA_KEYs defined in this class are used they may only be
- * one of the following:
- * <ul>
- * <li>{@link #METADATA_KEY_ART}</li>
- * <li>{@link #METADATA_KEY_ALBUM_ART}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
- * </ul>
- * Large bitmaps may be scaled down by the system when
- * {@link android.media.session.MediaSession#setMetadata} is called.
- * To pass full resolution images {@link Uri Uris} should be used with
- * {@link #putString}.
- *
- * @param key The key for referencing this value
- * @param value The Bitmap to store
- * @return The Builder to allow chaining
- */
- public @NonNull Builder putBitmap(@NonNull @BitmapKey String key, @Nullable Bitmap value) {
- return mProvider.putBitmap_impl(key, value);
- }
-
- /**
- * Put a float value into the metadata. Custom keys may be used.
- *
- * @param key The key for referencing this value
- * @param value The float value to store
- * @return The Builder to allow chaining
- */
- public @NonNull Builder putFloat(@NonNull @LongKey String key, float value) {
- return mProvider.putFloat_impl(key, value);
- }
-
- /**
- * Set a bundle of extras.
- *
- * @param extras The extras to include with this description or null.
- * @return The Builder to allow chaining
- */
- public Builder setExtras(@Nullable Bundle extras) {
- return mProvider.setExtras_impl(extras);
- }
-
- /**
- * Creates a {@link MediaMetadata2} instance with the specified fields.
- *
- * @return The new MediaMetadata2 instance
- */
- public @NonNull MediaMetadata2 build() {
- return mProvider.build_impl();
- }
- }
-}
-
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 548dc88..b047f8d 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;
@@ -35,6 +36,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;
@@ -411,6 +413,9 @@
mHandlerThread = null;
}
+ setCurrentSourceInfo(null);
+ clearNextSourceInfos();
+
// Modular DRM clean up
mOnDrmConfigHelper = null;
synchronized (mDrmEventCbLock) {
@@ -456,10 +461,8 @@
synchronized (mDrmEventCbLock) {
mDrmEventCallbackRecords.clear();
}
- synchronized (mSrcLock) {
- mCurrentSourceInfo = null;
- mNextSourceInfos.clear();
- }
+ setCurrentSourceInfo(null);
+ clearNextSourceInfos();
synchronized (mTaskLock) {
mPendingTasks.clear();
@@ -684,6 +687,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,15 +698,19 @@
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(new SourceInfo(dsd));
+ handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
+ }
+ } finally {
+ dsd.close();
}
}
});
@@ -710,6 +719,8 @@
/**
* 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 +730,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();
mNextSourceInfos.add(new SourceInfo(dsd));
}
prepareNextDataSource();
@@ -731,6 +742,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 +756,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();
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 +781,7 @@
return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
@Override
void process() {
- mNextSourceInfos.clear();
+ clearNextSourceInfos();
}
});
}
@@ -788,20 +799,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 +896,7 @@
if (afd.getDeclaredLength() < 0) {
handleDataSource(isCurrent,
srcId,
- afd.getFileDescriptor(),
+ ParcelFileDescriptor.dup(afd.getFileDescriptor()),
0,
DataSourceDesc.LONG_MAX,
startPos,
@@ -893,7 +904,7 @@
} else {
handleDataSource(isCurrent,
srcId,
- afd.getFileDescriptor(),
+ ParcelFileDescriptor.dup(afd.getFileDescriptor()),
afd.getStartOffset(),
afd.getDeclaredLength(),
startPos,
@@ -959,7 +970,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 +995,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 +1007,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 +1049,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 +1073,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(mNextSourceInfos.poll());
long srcId = mCurrentSourceInfo.mId;
try {
@@ -1513,7 +1529,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 +1552,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 +1580,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);
}
});
@@ -1763,10 +1779,13 @@
*/
// This is an asynchronous call.
public Object setAudioSessionId(int sessionId) {
- keepAudioSessionIdAlive(sessionId);
+ final AudioTrack dummyAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
+ AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2,
+ AudioTrack.MODE_STATIC, sessionId);
return addTask(new Task(CALL_COMPLETED_SET_AUDIO_SESSION_ID, false) {
@Override
void process() {
+ keepAudioSessionIdAlive(dummyAudioTrack);
native_setAudioSessionId(sessionId);
}
});
@@ -2687,12 +2706,6 @@
}
}
- private static void checkArgument(boolean expression, String errorMessage) {
- if (!expression) {
- throw new IllegalArgumentException(errorMessage);
- }
- }
-
private void sendEvent(final EventNotifier notifier) {
synchronized (mEventCbLock) {
try {
@@ -3318,6 +3331,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
*
@@ -3625,7 +3657,7 @@
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
@@ -3783,7 +3815,7 @@
@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);
@@ -3826,7 +3858,7 @@
// 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);
@@ -4473,6 +4505,7 @@
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}
@@ -4484,6 +4517,17 @@
this.mDSD = dsd;
}
+ void close() {
+ synchronized (this) {
+ if (!mClosed) {
+ if (mDSD != null) {
+ mDSD.close();
+ }
+ mClosed = true;
+ }
+ }
+ }
+
@Override
public String toString() {
return String.format("%s(%d)", SourceInfo.class.getName(), mId);
@@ -4514,6 +4558,26 @@
return nextSourceInfo != null && nextSourceInfo.mId == srcId;
}
+ private void setCurrentSourceInfo(SourceInfo newSourceInfo) {
+ synchronized (mSrcLock) {
+ if (mCurrentSourceInfo != null) {
+ mCurrentSourceInfo.close();
+ }
+ mCurrentSourceInfo = newSourceInfo;
+ }
+ }
+
+ private void clearNextSourceInfos() {
+ synchronized (mSrcLock) {
+ for (SourceInfo sourceInfo : mNextSourceInfos) {
+ if (sourceInfo != null) {
+ sourceInfo.close();
+ }
+ }
+ mNextSourceInfos.clear();
+ }
+ }
+
public static final class MetricsConstants {
private MetricsConstants() {}
@@ -4621,4 +4685,17 @@
AudioTrack.MODE_STATIC, sessionId);
}
}
+
+ private void keepAudioSessionIdAlive(AudioTrack at) {
+ synchronized (mSessionIdLock) {
+ if (mDummyAudioTrack != null) {
+ if (mDummyAudioTrack.getAudioSessionId() == at.getAudioSessionId()) {
+ at.release();
+ return;
+ }
+ mDummyAudioTrack.release();
+ }
+ mDummyAudioTrack = at;
+ }
+ }
}
diff --git a/media/java/android/media/MediaPlaylistAgent.java b/media/java/android/media/MediaPlaylistAgent.java
deleted file mode 100644
index 88f37e7..0000000
--- a/media/java/android/media/MediaPlaylistAgent.java
+++ /dev/null
@@ -1,357 +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.media;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.media.update.ApiLoader;
-import android.media.update.MediaPlaylistAgentProvider;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- * MediaPlaylistAgent is the abstract class an application needs to derive from to pass an object
- * to a MediaSession2 that will override default playlist handling behaviors. It contains a set of
- * notify methods to signal MediaSession2 that playlist-related state has changed.
- * <p>
- * Playlists are composed of one or multiple {@link MediaItem2} instances, which combine metadata
- * and data sources (as {@link DataSourceDesc})
- * Used by {@link MediaSession2} and {@link MediaController2}.
- */
-// This class only includes methods that contain {@link MediaItem2}.
-public abstract class MediaPlaylistAgent {
- /**
- * @hide
- */
- @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL,
- REPEAT_MODE_GROUP})
- @Retention(RetentionPolicy.SOURCE)
- public @interface RepeatMode {}
-
- /**
- * Playback will be stopped at the end of the playing media list.
- */
- public static final int REPEAT_MODE_NONE = 0;
-
- /**
- * Playback of the current playing media item will be repeated.
- */
- public static final int REPEAT_MODE_ONE = 1;
-
- /**
- * Playing media list will be repeated.
- */
- public static final int REPEAT_MODE_ALL = 2;
-
- /**
- * Playback of the playing media group will be repeated.
- * A group is a logical block of media items which is specified in the section 5.7 of the
- * Bluetooth AVRCP 1.6. An example of a group is the playlist.
- */
- public static final int REPEAT_MODE_GROUP = 3;
-
- /**
- * @hide
- */
- @IntDef({SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP})
- @Retention(RetentionPolicy.SOURCE)
- public @interface ShuffleMode {}
-
- /**
- * Media list will be played in order.
- */
- public static final int SHUFFLE_MODE_NONE = 0;
-
- /**
- * Media list will be played in shuffled order.
- */
- public static final int SHUFFLE_MODE_ALL = 1;
-
- /**
- * Media group will be played in shuffled order.
- * A group is a logical block of media items which is specified in the section 5.7 of the
- * Bluetooth AVRCP 1.6. An example of a group is the playlist.
- */
- public static final int SHUFFLE_MODE_GROUP = 2;
-
- private final MediaPlaylistAgentProvider mProvider;
-
- /**
- * A callback class to receive notifications for events on the media player. See
- * {@link MediaPlaylistAgent#registerPlaylistEventCallback(Executor, PlaylistEventCallback)}
- * to register this callback.
- */
- public static abstract class PlaylistEventCallback {
- /**
- * Called when a playlist is changed.
- *
- * @param playlistAgent playlist agent for this event
- * @param list new playlist
- * @param metadata new metadata
- */
- public void onPlaylistChanged(@NonNull MediaPlaylistAgent playlistAgent,
- @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) { }
-
- /**
- * Called when a playlist metadata is changed.
- *
- * @param playlistAgent playlist agent for this event
- * @param metadata new metadata
- */
- public void onPlaylistMetadataChanged(@NonNull MediaPlaylistAgent playlistAgent,
- @Nullable MediaMetadata2 metadata) { }
-
- /**
- * Called when the shuffle mode is changed.
- *
- * @param playlistAgent playlist agent for this event
- * @param shuffleMode repeat mode
- * @see #SHUFFLE_MODE_NONE
- * @see #SHUFFLE_MODE_ALL
- * @see #SHUFFLE_MODE_GROUP
- */
- public void onShuffleModeChanged(@NonNull MediaPlaylistAgent playlistAgent,
- @ShuffleMode int shuffleMode) { }
-
- /**
- * Called when the repeat mode is changed.
- *
- * @param playlistAgent playlist agent for this event
- * @param repeatMode repeat mode
- * @see #REPEAT_MODE_NONE
- * @see #REPEAT_MODE_ONE
- * @see #REPEAT_MODE_ALL
- * @see #REPEAT_MODE_GROUP
- */
- public void onRepeatModeChanged(@NonNull MediaPlaylistAgent playlistAgent,
- @RepeatMode int repeatMode) { }
- }
-
- public MediaPlaylistAgent() {
- mProvider = ApiLoader.getProvider().createMediaPlaylistAgent(this);
- }
-
- /**
- * Register {@link PlaylistEventCallback} to listen changes in the underlying
- * {@link MediaPlaylistAgent}.
- *
- * @param executor a callback Executor
- * @param callback a PlaylistEventCallback
- * @throws IllegalArgumentException if executor or callback is {@code null}.
- */
- public final void registerPlaylistEventCallback(
- @NonNull @CallbackExecutor Executor executor, @NonNull PlaylistEventCallback callback) {
- mProvider.registerPlaylistEventCallback_impl(executor, callback);
- }
-
- /**
- * Unregister the previously registered {@link PlaylistEventCallback}.
- *
- * @param callback the callback to be removed
- * @throws IllegalArgumentException if the callback is {@code null}.
- */
- public final void unregisterPlaylistEventCallback(@NonNull PlaylistEventCallback callback) {
- mProvider.unregisterPlaylistEventCallback_impl(callback);
- }
-
- public final void notifyPlaylistChanged() {
- mProvider.notifyPlaylistChanged_impl();
- }
-
- public final void notifyPlaylistMetadataChanged() {
- mProvider.notifyPlaylistMetadataChanged_impl();
- }
-
- public final void notifyShuffleModeChanged() {
- mProvider.notifyShuffleModeChanged_impl();
- }
-
- public final void notifyRepeatModeChanged() {
- mProvider.notifyRepeatModeChanged_impl();
- }
-
- /**
- * Returns the playlist
- *
- * @return playlist, or null if none is set.
- */
- public @Nullable List<MediaItem2> getPlaylist() {
- return mProvider.getPlaylist_impl();
- }
-
- /**
- * Sets the playlist.
- *
- * @param list playlist
- * @param metadata metadata of the playlist
- */
- public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
- mProvider.setPlaylist_impl(list, metadata);
- }
-
- /**
- * Returns the playlist metadata
- *
- * @return metadata metadata of the playlist, or null if none is set
- */
- public @Nullable MediaMetadata2 getPlaylistMetadata() {
- return mProvider.getPlaylistMetadata_impl();
- }
-
- /**
- * Updates the playlist metadata
- *
- * @param metadata metadata of the playlist
- */
- public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
- mProvider.updatePlaylistMetadata_impl(metadata);
- }
-
- /**
- * Adds the media item to the playlist at position index. Index equals or greater than
- * the current playlist size will add the item at the end of the playlist.
- * <p>
- * This will not change the currently playing media item.
- * If index is less than or equal to the current index of the playlist,
- * the current index of the playlist will be incremented correspondingly.
- *
- * @param index the index you want to add
- * @param item the media item you want to add
- */
- public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
- mProvider.addPlaylistItem_impl(index, item);
- }
-
- /**
- * Removes the media item from the playlist
- *
- * @param item media item to remove
- */
- public void removePlaylistItem(@NonNull MediaItem2 item) {
- mProvider.removePlaylistItem_impl(item);
- }
-
- /**
- * Replace the media item at index in the playlist. This can be also used to update metadata of
- * an item.
- *
- * @param index the index of the item to replace
- * @param item the new item
- */
- public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
- mProvider.replacePlaylistItem_impl(index, item);
- }
-
- /**
- * Skips to the the media item, and plays from it.
- *
- * @param item media item to start playing from
- */
- public void skipToPlaylistItem(@NonNull MediaItem2 item) {
- mProvider.skipToPlaylistItem_impl(item);
- }
-
- /**
- * Skips to the previous item in the playlist.
- */
- public void skipToPreviousItem() {
- mProvider.skipToPreviousItem_impl();
- }
-
- /**
- * Skips to the next item in the playlist.
- */
- public void skipToNextItem() {
- mProvider.skipToNextItem_impl();
- }
-
- /**
- * Gets the repeat mode
- *
- * @return repeat mode
- * @see #REPEAT_MODE_NONE
- * @see #REPEAT_MODE_ONE
- * @see #REPEAT_MODE_ALL
- * @see #REPEAT_MODE_GROUP
- */
- public @RepeatMode int getRepeatMode() {
- return mProvider.getRepeatMode_impl();
- }
-
- /**
- * Sets the repeat mode
- *
- * @param repeatMode repeat mode
- * @see #REPEAT_MODE_NONE
- * @see #REPEAT_MODE_ONE
- * @see #REPEAT_MODE_ALL
- * @see #REPEAT_MODE_GROUP
- */
- public void setRepeatMode(@RepeatMode int repeatMode) {
- mProvider.setRepeatMode_impl(repeatMode);
- }
-
- /**
- * Gets the shuffle mode
- *
- * @return The shuffle mode
- * @see #SHUFFLE_MODE_NONE
- * @see #SHUFFLE_MODE_ALL
- * @see #SHUFFLE_MODE_GROUP
- */
- public @ShuffleMode int getShuffleMode() {
- return mProvider.getShuffleMode_impl();
- }
-
- /**
- * Sets the shuffle mode
- *
- * @param shuffleMode The shuffle mode
- * @see #SHUFFLE_MODE_NONE
- * @see #SHUFFLE_MODE_ALL
- * @see #SHUFFLE_MODE_GROUP
- */
- public void setShuffleMode(@ShuffleMode int shuffleMode) {
- mProvider.setShuffleMode_impl(shuffleMode);
- }
-
- /**
- * Called by {@link MediaSession2} when it wants to translate {@link DataSourceDesc} from the
- * {@link MediaPlayerBase.PlayerEventCallback} to the {@link MediaItem2}. Override this method
- * if you want to create {@link DataSourceDesc}s dynamically, instead of specifying them with
- * {@link #setPlaylist(List, MediaMetadata2)}.
- * <p>
- * Session would throw an exception if this returns {@code null} for {@param dsd} from the
- * {@link MediaPlayerBase.PlayerEventCallback}.
- * <p>
- * Default implementation calls the {@link #getPlaylist()} and searches the {@link MediaItem2}
- * with the {@param dsd}.
- *
- * @param dsd The dsd to query.
- * @return A {@link MediaItem2} object in the playlist that matches given {@code dsd}.
- * @throws IllegalArgumentException if {@code dsd} is null
- */
- public @Nullable MediaItem2 getMediaItem(@NonNull DataSourceDesc dsd) {
- return mProvider.getMediaItem_impl(dsd);
- }
-}
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
deleted file mode 100644
index 9e97125..0000000
--- a/media/java/android/media/MediaSession2.java
+++ /dev/null
@@ -1,1388 +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.media;
-
-import static android.media.MediaPlayerBase.BUFFERING_STATE_UNKNOWN;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.media.MediaPlayerBase.BuffState;
-import android.media.MediaPlayerBase.PlayerState;
-import android.media.MediaPlaylistAgent.RepeatMode;
-import android.media.MediaPlaylistAgent.ShuffleMode;
-import android.media.update.ApiLoader;
-import android.media.update.MediaSession2Provider;
-import android.media.update.MediaSession2Provider.BuilderBaseProvider;
-import android.media.update.MediaSession2Provider.CommandButtonProvider;
-import android.media.update.MediaSession2Provider.ControllerInfoProvider;
-import android.media.update.ProviderCreator;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IInterface;
-import android.os.ResultReceiver;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- * Allows a media app to expose its transport controls and playback information in a process to
- * other processes including the Android framework and other apps. Common use cases are as follows.
- * <ul>
- * <li>Bluetooth/wired headset key events support</li>
- * <li>Android Auto/Wearable support</li>
- * <li>Separating UI process and playback process</li>
- * </ul>
- * <p>
- * A MediaSession2 should be created when an app wants to publish media playback information or
- * handle media keys. In general an app only needs one session for all playback, though multiple
- * sessions can be created to provide finer grain controls of media.
- * <p>
- * A session can be obtained by {@link Builder}. The owner of the session may pass its session token
- * to other processes to allow them to create a {@link MediaController2} to interact with the
- * session.
- * <p>
- * When a session receive transport control commands, the session sends the commands directly to
- * the the underlying media player set by {@link Builder} or
- * {@link #updatePlayer}.
- * <p>
- * When an app is finished performing playback it must call {@link #close()} to clean up the session
- * and notify any controllers.
- * <p>
- * {@link MediaSession2} objects should be used on the thread on the looper.
- */
-public class MediaSession2 implements AutoCloseable {
- private final MediaSession2Provider mProvider;
-
- /**
- * @hide
- */
- @IntDef({ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_APP_ERROR, ERROR_CODE_NOT_SUPPORTED,
- ERROR_CODE_AUTHENTICATION_EXPIRED, ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED,
- ERROR_CODE_CONCURRENT_STREAM_LIMIT, ERROR_CODE_PARENTAL_CONTROL_RESTRICTED,
- ERROR_CODE_NOT_AVAILABLE_IN_REGION, ERROR_CODE_CONTENT_ALREADY_PLAYING,
- ERROR_CODE_SKIP_LIMIT_REACHED, ERROR_CODE_ACTION_ABORTED, ERROR_CODE_END_OF_QUEUE,
- ERROR_CODE_SETUP_REQUIRED})
- @Retention(RetentionPolicy.SOURCE)
- public @interface ErrorCode {}
-
- /**
- * This is the default error code and indicates that none of the other error codes applies.
- */
- public static final int ERROR_CODE_UNKNOWN_ERROR = 0;
-
- /**
- * Error code when the application state is invalid to fulfill the request.
- */
- public static final int ERROR_CODE_APP_ERROR = 1;
-
- /**
- * Error code when the request is not supported by the application.
- */
- public static final int ERROR_CODE_NOT_SUPPORTED = 2;
-
- /**
- * Error code when the request cannot be performed because authentication has expired.
- */
- public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3;
-
- /**
- * Error code when a premium account is required for the request to succeed.
- */
- public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4;
-
- /**
- * Error code when too many concurrent streams are detected.
- */
- public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5;
-
- /**
- * Error code when the content is blocked due to parental controls.
- */
- public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6;
-
- /**
- * Error code when the content is blocked due to being regionally unavailable.
- */
- public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7;
-
- /**
- * Error code when the requested content is already playing.
- */
- public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8;
-
- /**
- * Error code when the application cannot skip any more songs because skip limit is reached.
- */
- public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9;
-
- /**
- * Error code when the action is interrupted due to some external event.
- */
- public static final int ERROR_CODE_ACTION_ABORTED = 10;
-
- /**
- * Error code when the playback navigation (previous, next) is not possible because the queue
- * was exhausted.
- */
- public static final int ERROR_CODE_END_OF_QUEUE = 11;
-
- /**
- * Error code when the session needs user's manual intervention.
- */
- public static final int ERROR_CODE_SETUP_REQUIRED = 12;
-
- /**
- * Interface definition of a callback to be invoked when a {@link MediaItem2} in the playlist
- * didn't have a {@link DataSourceDesc} but it's needed now for preparing or playing it.
- *
- * #see #setOnDataSourceMissingHelper
- */
- public interface OnDataSourceMissingHelper {
- /**
- * Called when a {@link MediaItem2} in the playlist didn't have a {@link DataSourceDesc}
- * but it's needed now for preparing or playing it. Returned data source descriptor will be
- * sent to the player directly to prepare or play the contents.
- * <p>
- * An exception may be thrown if the returned {@link DataSourceDesc} is duplicated in the
- * playlist, so items cannot be differentiated.
- *
- * @param session the session for this event
- * @param item media item from the controller
- * @return a data source descriptor if the media item. Can be {@code null} if the content
- * isn't available.
- */
- @Nullable DataSourceDesc onDataSourceMissing(@NonNull MediaSession2 session,
- @NonNull MediaItem2 item);
- }
-
- /**
- * Callback to be called for all incoming commands from {@link MediaController2}s.
- * <p>
- * If it's not set, the session will accept all controllers and all incoming commands by
- * default.
- */
- // TODO(jaewan): Move this to updatable for default implementation (b/74091963)
- public static abstract class SessionCallback {
- /**
- * Called when a controller is created for this session. Return allowed commands for
- * controller. By default it allows all connection requests and commands.
- * <p>
- * You can reject the connection by return {@code null}. In that case, controller receives
- * {@link MediaController2.ControllerCallback#onDisconnected(MediaController2)} and cannot
- * be usable.
- *
- * @param session the session for this event
- * @param controller controller information.
- * @return allowed commands. Can be {@code null} to reject connection.
- */
- public @Nullable SessionCommandGroup2 onConnect(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller) {
- SessionCommandGroup2 commands = new SessionCommandGroup2();
- commands.addAllPredefinedCommands();
- return commands;
- }
-
- /**
- * Called when a controller is disconnected
- *
- * @param session the session for this event
- * @param controller controller information
- */
- public void onDisconnected(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller) { }
-
- /**
- * Called when a controller sent a command that will be sent directly to the player. Return
- * {@code false} here to reject the request and stop sending command to the player.
- *
- * @param session the session for this event
- * @param controller controller information.
- * @param command a command. This method will be called for every single command.
- * @return {@code true} if you want to accept incoming command. {@code false} otherwise.
- * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PLAY
- * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PAUSE
- * @see SessionCommand2#COMMAND_CODE_PLAYBACK_STOP
- * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM
- * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM
- * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PREPARE
- * @see SessionCommand2#COMMAND_CODE_SESSION_FAST_FORWARD
- * @see SessionCommand2#COMMAND_CODE_SESSION_REWIND
- * @see SessionCommand2#COMMAND_CODE_PLAYBACK_SEEK_TO
- * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM
- * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
- * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REMOVE_ITEM
- * @see SessionCommand2#COMMAND_CODE_PLAYLIST_GET_LIST
- * @see SessionCommand2#COMMAND_CODE_SET_VOLUME
- */
- public boolean onCommandRequest(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller, @NonNull SessionCommand2 command) {
- return true;
- }
-
- /**
- * Called when a controller set rating of a media item through
- * {@link MediaController2#setRating(String, Rating2)}.
- * <p>
- * To allow setting user rating for a {@link MediaItem2}, the media item's metadata
- * should have {@link Rating2} with the key {@link MediaMetadata#METADATA_KEY_USER_RATING},
- * in order to provide possible rating style for controller. Controller will follow the
- * rating style.
- *
- * @param session the session for this event
- * @param controller controller information
- * @param mediaId media id from the controller
- * @param rating new rating from the controller
- */
- public void onSetRating(@NonNull MediaSession2 session, @NonNull ControllerInfo controller,
- @NonNull String mediaId, @NonNull Rating2 rating) { }
-
- /**
- * Called when a controller sent a custom command through
- * {@link MediaController2#sendCustomCommand(SessionCommand2, Bundle, ResultReceiver)}.
- *
- * @param session the session for this event
- * @param controller controller information
- * @param customCommand custom command.
- * @param args optional arguments
- * @param cb optional result receiver
- */
- public void onCustomCommand(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller, @NonNull SessionCommand2 customCommand,
- @Nullable Bundle args, @Nullable ResultReceiver cb) { }
-
- /**
- * Called when a controller requested to play a specific mediaId through
- * {@link MediaController2#playFromMediaId(String, Bundle)}.
- *
- * @param session the session for this event
- * @param controller controller information
- * @param mediaId media id
- * @param extras optional extra bundle
- * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID
- */
- public void onPlayFromMediaId(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller, @NonNull String mediaId,
- @Nullable Bundle extras) { }
-
- /**
- * Called when a controller requested to begin playback from a search query through
- * {@link MediaController2#playFromSearch(String, Bundle)}
- * <p>
- * An empty query indicates that the app may play any music. The implementation should
- * attempt to make a smart choice about what to play.
- *
- * @param session the session for this event
- * @param controller controller information
- * @param query query string. Can be empty to indicate any suggested media
- * @param extras optional extra bundle
- * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_SEARCH
- */
- public void onPlayFromSearch(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller, @NonNull String query,
- @Nullable Bundle extras) { }
-
- /**
- * Called when a controller requested to play a specific media item represented by a URI
- * through {@link MediaController2#playFromUri(Uri, Bundle)}
- *
- * @param session the session for this event
- * @param controller controller information
- * @param uri uri
- * @param extras optional extra bundle
- * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_URI
- */
- public void onPlayFromUri(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller, @NonNull Uri uri,
- @Nullable Bundle extras) { }
-
- /**
- * Called when a controller requested to prepare for playing a specific mediaId through
- * {@link MediaController2#prepareFromMediaId(String, Bundle)}.
- * <p>
- * During the preparation, a session should not hold audio focus in order to allow other
- * sessions play seamlessly. The state of playback should be updated to
- * {@link MediaPlayerBase#PLAYER_STATE_PAUSED} after the preparation is done.
- * <p>
- * The playback of the prepared content should start in the later calls of
- * {@link MediaSession2#play()}.
- * <p>
- * Override {@link #onPlayFromMediaId} to handle requests for starting
- * playback without preparation.
- *
- * @param session the session for this event
- * @param controller controller information
- * @param mediaId media id to prepare
- * @param extras optional extra bundle
- * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID
- */
- public void onPrepareFromMediaId(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller, @NonNull String mediaId,
- @Nullable Bundle extras) { }
-
- /**
- * Called when a controller requested to prepare playback from a search query through
- * {@link MediaController2#prepareFromSearch(String, Bundle)}.
- * <p>
- * An empty query indicates that the app may prepare any music. The implementation should
- * attempt to make a smart choice about what to play.
- * <p>
- * The state of playback should be updated to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}
- * after the preparation is done. The playback of the prepared content should start in the
- * later calls of {@link MediaSession2#play()}.
- * <p>
- * Override {@link #onPlayFromSearch} to handle requests for starting playback without
- * preparation.
- *
- * @param session the session for this event
- * @param controller controller information
- * @param query query string. Can be empty to indicate any suggested media
- * @param extras optional extra bundle
- * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH
- */
- public void onPrepareFromSearch(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller, @NonNull String query,
- @Nullable Bundle extras) { }
-
- /**
- * Called when a controller requested to prepare a specific media item represented by a URI
- * through {@link MediaController2#prepareFromUri(Uri, Bundle)}.
- * <p>
- * During the preparation, a session should not hold audio focus in order to allow
- * other sessions play seamlessly. The state of playback should be updated to
- * {@link MediaPlayerBase#PLAYER_STATE_PAUSED} after the preparation is done.
- * <p>
- * The playback of the prepared content should start in the later calls of
- * {@link MediaSession2#play()}.
- * <p>
- * Override {@link #onPlayFromUri} to handle requests for starting playback without
- * preparation.
- *
- * @param session the session for this event
- * @param controller controller information
- * @param uri uri
- * @param extras optional extra bundle
- * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_URI
- */
- public void onPrepareFromUri(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller, @NonNull Uri uri, @Nullable Bundle extras) { }
-
- /**
- * Called when a controller called {@link MediaController2#fastForward()}
- *
- * @param session the session for this event
- */
- public void onFastForward(@NonNull MediaSession2 session) { }
-
- /**
- * Called when a controller called {@link MediaController2#rewind()}
- *
- * @param session the session for this event
- */
- public void onRewind(@NonNull MediaSession2 session) { }
-
- /**
- * Called when the player's current playing item is changed
- * <p>
- * When it's called, you should invalidate previous playback information and wait for later
- * callbacks.
- *
- * @param session the controller for this event
- * @param player the player for this event
- * @param item new item
- */
- // TODO(jaewan): Use this (b/74316764)
- public void onCurrentMediaItemChanged(@NonNull MediaSession2 session,
- @NonNull MediaPlayerBase player, @NonNull MediaItem2 item) { }
-
- /**
- * Called when the player is <i>prepared</i>, i.e. it is ready to play the content
- * referenced by the given data source.
- * @param session the session for this event
- * @param player the player for this event
- * @param item the media item for which buffering is happening
- */
- public void onMediaPrepared(@NonNull MediaSession2 session, @NonNull MediaPlayerBase player,
- @NonNull MediaItem2 item) { }
-
- /**
- * Called to indicate that the state of the player has changed.
- * See {@link MediaPlayerBase#getPlayerState()} for polling the player state.
- * @param session the session for this event
- * @param player the player for this event
- * @param state the new state of the player.
- */
- public void onPlayerStateChanged(@NonNull MediaSession2 session,
- @NonNull MediaPlayerBase player, @PlayerState int state) { }
-
- /**
- * Called to report buffering events for a data source.
- *
- * @param session the session for this event
- * @param player the player for this event
- * @param item the media item for which buffering is happening.
- * @param state the new buffering state.
- */
- public void onBufferingStateChanged(@NonNull MediaSession2 session,
- @NonNull MediaPlayerBase player, @NonNull MediaItem2 item, @BuffState int state) { }
-
- /**
- * Called to indicate that the playback speed has changed.
- * @param session the session for this event
- * @param player the player for this event
- * @param speed the new playback speed.
- */
- public void onPlaybackSpeedChanged(@NonNull MediaSession2 session,
- @NonNull MediaPlayerBase player, float speed) { }
-
- /**
- * Called to indicate that {@link #seekTo(long)} is completed.
- *
- * @param session the session for this event.
- * @param mpb the player that has completed seeking.
- * @param position the previous seeking request.
- * @see #seekTo(long)
- */
- public void onSeekCompleted(@NonNull MediaSession2 session, @NonNull MediaPlayerBase mpb,
- long position) { }
-
- /**
- * Called when a playlist is changed from the {@link MediaPlaylistAgent}.
- * <p>
- * This is called when the underlying agent has called
- * {@link MediaPlaylistAgent.PlaylistEventCallback#onPlaylistChanged(MediaPlaylistAgent,
- * List, MediaMetadata2)}.
- *
- * @param session the session for this event
- * @param playlistAgent playlist agent for this event
- * @param list new playlist
- * @param metadata new metadata
- */
- public void onPlaylistChanged(@NonNull MediaSession2 session,
- @NonNull MediaPlaylistAgent playlistAgent, @NonNull List<MediaItem2> list,
- @Nullable MediaMetadata2 metadata) { }
-
- /**
- * Called when a playlist metadata is changed.
- *
- * @param session the session for this event
- * @param playlistAgent playlist agent for this event
- * @param metadata new metadata
- */
- public void onPlaylistMetadataChanged(@NonNull MediaSession2 session,
- @NonNull MediaPlaylistAgent playlistAgent, @Nullable MediaMetadata2 metadata) { }
-
- /**
- * Called when the shuffle mode is changed.
- *
- * @param session the session for this event
- * @param playlistAgent playlist agent for this event
- * @param shuffleMode repeat mode
- * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
- * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
- * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
- */
- public void onShuffleModeChanged(@NonNull MediaSession2 session,
- @NonNull MediaPlaylistAgent playlistAgent,
- @MediaPlaylistAgent.ShuffleMode int shuffleMode) { }
-
- /**
- * Called when the repeat mode is changed.
- *
- * @param session the session for this event
- * @param playlistAgent playlist agent for this event
- * @param repeatMode repeat mode
- * @see MediaPlaylistAgent#REPEAT_MODE_NONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ALL
- * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
- */
- public void onRepeatModeChanged(@NonNull MediaSession2 session,
- @NonNull MediaPlaylistAgent playlistAgent,
- @MediaPlaylistAgent.RepeatMode int repeatMode) { }
- }
-
- /**
- * Base builder class for MediaSession2 and its subclass. Any change in this class should be
- * also applied to the subclasses {@link MediaSession2.Builder} and
- * {@link MediaLibraryService2.MediaLibrarySession.Builder}.
- * <p>
- * APIs here should be package private, but should have documentations for developers.
- * Otherwise, javadoc will generate documentation with the generic types such as follows.
- * <pre>U extends BuilderBase<T, U, C> setSessionCallback(Executor executor, C callback)</pre>
- * <p>
- * This class is hidden to prevent from generating test stub, which fails with
- * 'unexpected bound' because it tries to auto generate stub class as follows.
- * <pre>abstract static class BuilderBase<
- * T extends android.media.MediaSession2,
- * U extends android.media.MediaSession2.BuilderBase<
- * T, U, C extends android.media.MediaSession2.SessionCallback>, C></pre>
- * @hide
- */
- static abstract class BuilderBase
- <T extends MediaSession2, U extends BuilderBase<T, U, C>, C extends SessionCallback> {
- private final BuilderBaseProvider<T, C> mProvider;
-
- BuilderBase(ProviderCreator<BuilderBase<T, U, C>, BuilderBaseProvider<T, C>> creator) {
- mProvider = creator.createProvider(this);
- }
-
- /**
- * Sets the underlying {@link MediaPlayerBase} for this session to dispatch incoming event
- * to.
- *
- * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
- */
- @NonNull U setPlayer(@NonNull MediaPlayerBase player) {
- mProvider.setPlayer_impl(player);
- return (U) this;
- }
-
- /**
- * Sets the {@link MediaPlaylistAgent} for this session to manages playlist of the
- * underlying {@link MediaPlayerBase}. The playlist agent should manage
- * {@link MediaPlayerBase} for calling {@link MediaPlayerBase#setNextDataSources(List)}.
- * <p>
- * If the {@link MediaPlaylistAgent} isn't set, session will create the default playlist
- * agent.
- *
- * @param playlistAgent a {@link MediaPlaylistAgent} that manages playlist of the
- * {@code player}
- */
- U setPlaylistAgent(@NonNull MediaPlaylistAgent playlistAgent) {
- mProvider.setPlaylistAgent_impl(playlistAgent);
- return (U) this;
- }
-
- /**
- * Sets the {@link VolumeProvider2} for this session to handle volume events. If not set,
- * system will adjust the appropriate stream volume for this session's player.
- *
- * @param volumeProvider The provider that will receive volume button events.
- */
- @NonNull U setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
- mProvider.setVolumeProvider_impl(volumeProvider);
- return (U) this;
- }
-
- /**
- * Set an intent for launching UI for this Session. This can be used as a
- * quick link to an ongoing media screen. The intent should be for an
- * activity that may be started using {@link Context#startActivity(Intent)}.
- *
- * @param pi The intent to launch to show UI for this session.
- */
- @NonNull U setSessionActivity(@Nullable PendingIntent pi) {
- mProvider.setSessionActivity_impl(pi);
- return (U) this;
- }
-
- /**
- * Set ID of the session. If it's not set, an empty string with used to create a session.
- * <p>
- * Use this if and only if your app supports multiple playback at the same time and also
- * wants to provide external apps to have finer controls of them.
- *
- * @param id id of the session. Must be unique per package.
- * @throws IllegalArgumentException if id is {@code null}
- * @return
- */
- @NonNull U setId(@NonNull String id) {
- mProvider.setId_impl(id);
- return (U) this;
- }
-
- /**
- * Set callback for the session.
- *
- * @param executor callback executor
- * @param callback session callback.
- * @return
- */
- @NonNull U setSessionCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull C callback) {
- mProvider.setSessionCallback_impl(executor, callback);
- return (U) this;
- }
-
- /**
- * Build {@link MediaSession2}.
- *
- * @return a new session
- * @throws IllegalStateException if the session with the same id is already exists for the
- * package.
- */
- @NonNull T build() {
- return mProvider.build_impl();
- }
- }
-
- /**
- * Builder for {@link MediaSession2}.
- * <p>
- * Any incoming event from the {@link MediaController2} will be handled on the thread
- * that created session with the {@link Builder#build()}.
- */
- // Override all methods just to show them with the type instead of generics in Javadoc.
- // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
- public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
- public Builder(Context context) {
- super((instance) -> ApiLoader.getProvider().createMediaSession2Builder(
- context, (Builder) instance));
- }
-
- @Override
- public @NonNull Builder setPlayer(@NonNull MediaPlayerBase player) {
- return super.setPlayer(player);
- }
-
- @Override
- public Builder setPlaylistAgent(@NonNull MediaPlaylistAgent playlistAgent) {
- return super.setPlaylistAgent(playlistAgent);
- }
-
- @Override
- public @NonNull Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
- return super.setVolumeProvider(volumeProvider);
- }
-
- @Override
- public @NonNull Builder setSessionActivity(@Nullable PendingIntent pi) {
- return super.setSessionActivity(pi);
- }
-
- @Override
- public @NonNull Builder setId(@NonNull String id) {
- return super.setId(id);
- }
-
- @Override
- public @NonNull Builder setSessionCallback(@NonNull Executor executor,
- @Nullable SessionCallback callback) {
- return super.setSessionCallback(executor, callback);
- }
-
- @Override
- public @NonNull MediaSession2 build() {
- return super.build();
- }
- }
-
- /**
- * Information of a controller.
- */
- public static final class ControllerInfo {
- private final ControllerInfoProvider mProvider;
-
- /**
- * @hide
- */
- public ControllerInfo(@NonNull Context context, int uid, int pid,
- @NonNull String packageName, @NonNull IInterface callback) {
- mProvider = ApiLoader.getProvider().createMediaSession2ControllerInfo(
- context, this, uid, pid, packageName, callback);
- }
-
- /**
- * @return package name of the controller
- */
- public @NonNull String getPackageName() {
- return mProvider.getPackageName_impl();
- }
-
- /**
- * @return uid of the controller
- */
- public int getUid() {
- return mProvider.getUid_impl();
- }
-
- /**
- * Return if the controller has granted {@code android.permission.MEDIA_CONTENT_CONTROL} or
- * has a enabled notification listener so can be trusted to accept connection and incoming
- * command request.
- *
- * @return {@code true} if the controller is trusted.
- */
- public boolean isTrusted() {
- return mProvider.isTrusted_impl();
- }
-
- /**
- * @hide
- */
- public @NonNull ControllerInfoProvider getProvider() {
- return mProvider;
- }
-
- @Override
- public int hashCode() {
- return mProvider.hashCode_impl();
- }
-
- @Override
- public boolean equals(Object obj) {
- return mProvider.equals_impl(obj);
- }
-
- @Override
- public String toString() {
- return mProvider.toString_impl();
- }
- }
-
- /**
- * Button for a {@link SessionCommand2} that will be shown by the controller.
- * <p>
- * It's up to the controller's decision to respect or ignore this customization request.
- */
- public static final class CommandButton {
- private final CommandButtonProvider mProvider;
-
- /**
- * @hide
- */
- public CommandButton(CommandButtonProvider provider) {
- mProvider = provider;
- }
-
- /**
- * Get command associated with this button. Can be {@code null} if the button isn't enabled
- * and only providing placeholder.
- *
- * @return command or {@code null}
- */
- public @Nullable
- SessionCommand2 getCommand() {
- return mProvider.getCommand_impl();
- }
-
- /**
- * Resource id of the button in this package. Can be {@code 0} if the command is predefined
- * and custom icon isn't needed.
- *
- * @return resource id of the icon. Can be {@code 0}.
- */
- public int getIconResId() {
- return mProvider.getIconResId_impl();
- }
-
- /**
- * Display name of the button. Can be {@code null} or empty if the command is predefined
- * and custom name isn't needed.
- *
- * @return custom display name. Can be {@code null} or empty.
- */
- public @Nullable String getDisplayName() {
- return mProvider.getDisplayName_impl();
- }
-
- /**
- * Extra information of the button. It's private information between session and controller.
- *
- * @return
- */
- public @Nullable Bundle getExtras() {
- return mProvider.getExtras_impl();
- }
-
- /**
- * Return whether it's enabled
- *
- * @return {@code true} if enabled. {@code false} otherwise.
- */
- public boolean isEnabled() {
- return mProvider.isEnabled_impl();
- }
-
- /**
- * @hide
- */
- public @NonNull CommandButtonProvider getProvider() {
- return mProvider;
- }
-
- /**
- * Builder for {@link CommandButton}.
- */
- public static final class Builder {
- private final CommandButtonProvider.BuilderProvider mProvider;
-
- public Builder() {
- mProvider = ApiLoader.getProvider().createMediaSession2CommandButtonBuilder(this);
- }
-
- public @NonNull Builder setCommand(@Nullable SessionCommand2 command) {
- return mProvider.setCommand_impl(command);
- }
-
- public @NonNull Builder setIconResId(int resId) {
- return mProvider.setIconResId_impl(resId);
- }
-
- public @NonNull Builder setDisplayName(@Nullable String displayName) {
- return mProvider.setDisplayName_impl(displayName);
- }
-
- public @NonNull Builder setEnabled(boolean enabled) {
- return mProvider.setEnabled_impl(enabled);
- }
-
- public @NonNull Builder setExtras(@Nullable Bundle extras) {
- return mProvider.setExtras_impl(extras);
- }
-
- public @NonNull CommandButton build() {
- return mProvider.build_impl();
- }
- }
- }
-
- /**
- * Constructor is hidden and apps can only instantiate indirectly through {@link Builder}.
- * <p>
- * This intended behavior and here's the reasons.
- * 1. Prevent multiple sessions with the same tag in a media app.
- * Whenever it happens only one session was properly setup and others were all dummies.
- * Android framework couldn't find the right session to dispatch media key event.
- * 2. Simplify session's lifecycle.
- * {@link android.media.session.MediaSession} is available after all of
- * {@link android.media.session.MediaSession#setFlags(int)},
- * {@link android.media.session.MediaSession#setCallback(
- * android.media.session.MediaSession.Callback)},
- * and {@link android.media.session.MediaSession#setActive(boolean)}.
- * It was common for an app to omit one, so framework had to add heuristics to figure out
- * which should be the highest priority for handling media key event.
- * @hide
- */
- public MediaSession2(MediaSession2Provider provider) {
- super();
- mProvider = provider;
- }
-
- /**
- * @hide
- */
- public @NonNull MediaSession2Provider getProvider() {
- return mProvider;
- }
-
- /**
- * Sets the underlying {@link MediaPlayerBase} and {@link MediaPlaylistAgent} for this session
- * to dispatch incoming event to.
- * <p>
- * When a {@link MediaPlaylistAgent} is specified here, the playlist agent should manage
- * {@link MediaPlayerBase} for calling {@link MediaPlayerBase#setNextDataSources(List)}.
- * <p>
- * If the {@link MediaPlaylistAgent} isn't set, session will recreate the default playlist
- * agent.
- *
- * @param player a {@link MediaPlayerBase} that handles actual media playback in your app
- * @param playlistAgent a {@link MediaPlaylistAgent} that manages playlist of the {@code player}
- * @param volumeProvider a {@link VolumeProvider2}. If {@code null}, system will adjust the
- * appropriate stream volume for this session's player.
- */
- public void updatePlayer(@NonNull MediaPlayerBase player,
- @Nullable MediaPlaylistAgent playlistAgent, @Nullable VolumeProvider2 volumeProvider) {
- mProvider.updatePlayer_impl(player, playlistAgent, volumeProvider);
- }
-
- @Override
- public void close() {
- mProvider.close_impl();
- }
-
- /**
- * @return player
- */
- public @NonNull MediaPlayerBase getPlayer() {
- return mProvider.getPlayer_impl();
- }
-
- /**
- * @return playlist agent
- */
- public @NonNull MediaPlaylistAgent getPlaylistAgent() {
- return mProvider.getPlaylistAgent_impl();
- }
-
- /**
- * @return volume provider
- */
- public @Nullable VolumeProvider2 getVolumeProvider() {
- return mProvider.getVolumeProvider_impl();
- }
-
- /**
- * Returns the {@link SessionToken2} for creating {@link MediaController2}.
- */
- public @NonNull
- SessionToken2 getToken() {
- return mProvider.getToken_impl();
- }
-
- public @NonNull List<ControllerInfo> getConnectedControllers() {
- return mProvider.getConnectedControllers_impl();
- }
-
- /**
- * Set the {@link AudioFocusRequest} to obtain the audio focus
- *
- * @param afr the full request parameters
- */
- public void setAudioFocusRequest(@Nullable AudioFocusRequest afr) {
- // TODO(jaewan): implement this (b/72529899)
- // mProvider.setAudioFocusRequest_impl(focusGain);
- }
-
- /**
- * Sets ordered list of {@link CommandButton} for controllers to build UI with it.
- * <p>
- * It's up to controller's decision how to represent the layout in its own UI.
- * Here's the same way
- * (layout[i] means a CommandButton at index i in the given list)
- * For 5 icons row
- * layout[3] layout[1] layout[0] layout[2] layout[4]
- * For 3 icons row
- * layout[1] layout[0] layout[2]
- * For 5 icons row with overflow icon (can show +5 extra buttons with overflow button)
- * expanded row: layout[5] layout[6] layout[7] layout[8] layout[9]
- * main row: layout[3] layout[1] layout[0] layout[2] layout[4]
- * <p>
- * This API can be called in the {@link SessionCallback#onConnect(
- * MediaSession2, ControllerInfo)}.
- *
- * @param controller controller to specify layout.
- * @param layout ordered list of layout.
- */
- public void setCustomLayout(@NonNull ControllerInfo controller,
- @NonNull List<CommandButton> layout) {
- mProvider.setCustomLayout_impl(controller, layout);
- }
-
- /**
- * Set the new allowed command group for the controller
- *
- * @param controller controller to change allowed commands
- * @param commands new allowed commands
- */
- public void setAllowedCommands(@NonNull ControllerInfo controller,
- @NonNull SessionCommandGroup2 commands) {
- mProvider.setAllowedCommands_impl(controller, commands);
- }
-
- /**
- * Send custom command to all connected controllers.
- *
- * @param command a command
- * @param args optional argument
- */
- public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args) {
- mProvider.sendCustomCommand_impl(command, args);
- }
-
- /**
- * Send custom command to a specific controller.
- *
- * @param command a command
- * @param args optional argument
- * @param receiver result receiver for the session
- */
- public void sendCustomCommand(@NonNull ControllerInfo controller,
- @NonNull SessionCommand2 command, @Nullable Bundle args,
- @Nullable ResultReceiver receiver) {
- // Equivalent to the MediaController.sendCustomCommand(Action action, ResultReceiver r);
- mProvider.sendCustomCommand_impl(controller, command, args, receiver);
- }
-
- /**
- * Play playback
- * <p>
- * This calls {@link MediaPlayerBase#play()}.
- */
- public void play() {
- mProvider.play_impl();
- }
-
- /**
- * Pause playback.
- * <p>
- * This calls {@link MediaPlayerBase#pause()}.
- */
- public void pause() {
- mProvider.pause_impl();
- }
-
- /**
- * Stop playback, and reset the player to the initial state.
- * <p>
- * This calls {@link MediaPlayerBase#reset()}.
- */
- public void stop() {
- mProvider.stop_impl();
- }
-
- /**
- * Request that the player prepare its playback. In other words, other sessions can continue
- * to play during the preparation of this session. This method can be used to speed up the
- * start of the playback. Once the preparation is done, the session will change its playback
- * state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards, {@link #play} can be called
- * to start playback.
- * <p>
- * This calls {@link MediaPlayerBase#reset()}.
- */
- public void prepare() {
- mProvider.prepare_impl();
- }
-
- /**
- * Move to a new location in the media stream.
- *
- * @param pos Position to move to, in milliseconds.
- */
- public void seekTo(long pos) {
- mProvider.seekTo_impl(pos);
- }
-
- /**
- * @hide
- */
- public void skipForward() {
- // To match with KEYCODE_MEDIA_SKIP_FORWARD
- }
-
- /**
- * @hide
- */
- public void skipBackward() {
- // To match with KEYCODE_MEDIA_SKIP_BACKWARD
- }
-
- /**
- * Notify errors to the connected controllers
- *
- * @param errorCode error code
- * @param extras extras
- */
- public void notifyError(@ErrorCode int errorCode, @Nullable Bundle extras) {
- mProvider.notifyError_impl(errorCode, extras);
- }
-
- /**
- * Gets the current player state.
- *
- * @return the current player state
- */
- public @PlayerState int getPlayerState() {
- return mProvider.getPlayerState_impl();
- }
-
- /**
- * Gets the current position.
- *
- * @return the current playback position in ms, or {@link MediaPlayerBase#UNKNOWN_TIME} if
- * unknown.
- */
- public long getCurrentPosition() {
- return mProvider.getCurrentPosition_impl();
- }
-
- /**
- * Gets the buffered position, or {@link MediaPlayerBase#UNKNOWN_TIME} if unknown.
- *
- * @return the buffered position in ms, or {@link MediaPlayerBase#UNKNOWN_TIME}.
- */
- public long getBufferedPosition() {
- return mProvider.getBufferedPosition_impl();
- }
-
- /**
- * Gets the current buffering state of the player.
- * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
- * buffered.
- *
- * @return the buffering state.
- */
- public @BuffState int getBufferingState() {
- // TODO(jaewan): Implement this
- return BUFFERING_STATE_UNKNOWN;
- }
-
- /**
- * Get the playback speed.
- *
- * @return speed
- */
- public float getPlaybackSpeed() {
- // TODO(jaewan): implement this (b/74093080)
- return -1;
- }
-
- /**
- * Set the playback speed.
- */
- public void setPlaybackSpeed(float speed) {
- // TODO(jaewan): implement this (b/74093080)
- }
-
- /**
- * Sets the data source missing helper. Helper will be used to provide default implementation of
- * {@link MediaPlaylistAgent} when it isn't set by developer.
- * <p>
- * Default implementation of the {@link MediaPlaylistAgent} will call helper when a
- * {@link MediaItem2} in the playlist doesn't have a {@link DataSourceDesc}. This may happen
- * when
- * <ul>
- * <li>{@link MediaItem2} specified by {@link #setPlaylist(List, MediaMetadata2)} doesn't
- * have {@link DataSourceDesc}</li>
- * <li>{@link MediaController2#addPlaylistItem(int, MediaItem2)} is called and accepted
- * by {@link SessionCallback#onCommandRequest(
- * MediaSession2, ControllerInfo, SessionCommand2)}.
- * In that case, an item would be added automatically without the data source.</li>
- * </ul>
- * <p>
- * If it's not set, playback wouldn't happen for the item without data source descriptor.
- * <p>
- * The helper will be run on the executor that was specified by
- * {@link Builder#setSessionCallback(Executor, SessionCallback)}.
- *
- * @param helper a data source missing helper.
- * @throws IllegalStateException when the helper is set when the playlist agent is set
- * @see #setPlaylist(List, MediaMetadata2)
- * @see SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)
- * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
- * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REPLACE_ITEM
- */
- public void setOnDataSourceMissingHelper(@NonNull OnDataSourceMissingHelper helper) {
- mProvider.setOnDataSourceMissingHelper_impl(helper);
- }
-
- /**
- * Clears the data source missing helper.
- *
- * @see #setOnDataSourceMissingHelper(OnDataSourceMissingHelper)
- */
- public void clearOnDataSourceMissingHelper() {
- mProvider.clearOnDataSourceMissingHelper_impl();
- }
-
- /**
- * Returns the playlist from the {@link MediaPlaylistAgent}.
- * <p>
- * This list may differ with the list that was specified with
- * {@link #setPlaylist(List, MediaMetadata2)} depending on the {@link MediaPlaylistAgent}
- * implementation. Use media items returned here for other playlist agent APIs such as
- * {@link MediaPlaylistAgent#skipToPlaylistItem(MediaItem2)}.
- *
- * @return playlist
- * @see MediaPlaylistAgent#getPlaylist()
- * @see SessionCallback#onPlaylistChanged(
- * MediaSession2, MediaPlaylistAgent, List, MediaMetadata2)
- */
- public List<MediaItem2> getPlaylist() {
- return mProvider.getPlaylist_impl();
- }
-
- /**
- * Sets a list of {@link MediaItem2} to the {@link MediaPlaylistAgent}. Ensure uniqueness of
- * each {@link MediaItem2} in the playlist so the session can uniquely identity individual
- * items.
- * <p>
- * This may be an asynchronous call, and {@link MediaPlaylistAgent} may keep the copy of the
- * list. Wait for {@link SessionCallback#onPlaylistChanged(MediaSession2, MediaPlaylistAgent,
- * List, MediaMetadata2)} to know the operation finishes.
- * <p>
- * You may specify a {@link MediaItem2} without {@link DataSourceDesc}. In that case,
- * {@link MediaPlaylistAgent} has responsibility to dynamically query {@link DataSourceDesc}
- * when such media item is ready for preparation or play. Default implementation needs
- * {@link OnDataSourceMissingHelper} for such case.
- *
- * @param list A list of {@link MediaItem2} objects to set as a play list.
- * @throws IllegalArgumentException if given list is {@code null}, or has duplicated media
- * items.
- * @see MediaPlaylistAgent#setPlaylist(List, MediaMetadata2)
- * @see SessionCallback#onPlaylistChanged(
- * MediaSession2, MediaPlaylistAgent, List, MediaMetadata2)
- * @see #setOnDataSourceMissingHelper
- */
- public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
- mProvider.setPlaylist_impl(list, metadata);
- }
-
- /**
- * Skips to the item in the playlist.
- * <p>
- * This calls {@link MediaPlaylistAgent#skipToPlaylistItem(MediaItem2)} and the behavior depends
- * on the playlist agent implementation, especially with the shuffle/repeat mode.
- *
- * @param item The item in the playlist you want to play
- * @see #getShuffleMode()
- * @see #getRepeatMode()
- */
- public void skipToPlaylistItem(@NonNull MediaItem2 item) {
- mProvider.skipToPlaylistItem_impl(item);
- }
-
- /**
- * Skips to the previous item.
- * <p>
- * This calls {@link MediaPlaylistAgent#skipToPreviousItem()} and the behavior depends on the
- * playlist agent implementation, especially with the shuffle/repeat mode.
- *
- * @see #getShuffleMode()
- * @see #getRepeatMode()
- **/
- public void skipToPreviousItem() {
- mProvider.skipToPreviousItem_impl();
- }
-
- /**
- * Skips to the next item.
- * <p>
- * This calls {@link MediaPlaylistAgent#skipToNextItem()} and the behavior depends on the
- * playlist agent implementation, especially with the shuffle/repeat mode.
- *
- * @see #getShuffleMode()
- * @see #getRepeatMode()
- */
- public void skipToNextItem() {
- mProvider.skipToNextItem_impl();
- }
-
- /**
- * Gets the playlist metadata from the {@link MediaPlaylistAgent}.
- *
- * @return the playlist metadata
- */
- public MediaMetadata2 getPlaylistMetadata() {
- return mProvider.getPlaylistMetadata_impl();
- }
-
- /**
- * Adds the media item to the playlist at position index. Index equals or greater than
- * the current playlist size will add the item at the end of the playlist.
- * <p>
- * This will not change the currently playing media item.
- * If index is less than or equal to the current index of the play list,
- * the current index of the play list will be incremented correspondingly.
- *
- * @param index the index you want to add
- * @param item the media item you want to add
- */
- public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
- mProvider.addPlaylistItem_impl(index, item);
- }
-
- /**
- * Removes the media item in the playlist.
- * <p>
- * If the item is the currently playing item of the playlist, current playback
- * will be stopped and playback moves to next source in the list.
- *
- * @param item the media item you want to add
- */
- public void removePlaylistItem(@NonNull MediaItem2 item) {
- mProvider.removePlaylistItem_impl(item);
- }
-
- /**
- * Replaces the media item at index in the playlist. This can be also used to update metadata of
- * an item.
- *
- * @param index the index of the item to replace
- * @param item the new item
- */
- public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
- mProvider.replacePlaylistItem_impl(index, item);
- }
-
- /**
- * Return currently playing media item.
- *
- * @return currently playing media item
- */
- public MediaItem2 getCurrentMediaItem() {
- // TODO(jaewan): Rename provider, and implement (b/74316764)
- return mProvider.getCurrentPlaylistItem_impl();
- }
-
- /**
- * Updates the playlist metadata to the {@link MediaPlaylistAgent}.
- *
- * @param metadata metadata of the playlist
- */
- public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
- mProvider.updatePlaylistMetadata_impl(metadata);
- }
-
- /**
- * Gets the repeat mode from the {@link MediaPlaylistAgent}.
- *
- * @return repeat mode
- * @see MediaPlaylistAgent#REPEAT_MODE_NONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ALL
- * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
- */
- public @RepeatMode int getRepeatMode() {
- return mProvider.getRepeatMode_impl();
- }
-
- /**
- * Sets the repeat mode to the {@link MediaPlaylistAgent}.
- *
- * @param repeatMode repeat mode
- * @see MediaPlaylistAgent#REPEAT_MODE_NONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ONE
- * @see MediaPlaylistAgent#REPEAT_MODE_ALL
- * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
- */
- public void setRepeatMode(@RepeatMode int repeatMode) {
- mProvider.setRepeatMode_impl(repeatMode);
- }
-
- /**
- * Gets the shuffle mode from the {@link MediaPlaylistAgent}.
- *
- * @return The shuffle mode
- * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
- * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
- * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
- */
- public @ShuffleMode int getShuffleMode() {
- return mProvider.getShuffleMode_impl();
- }
-
- /**
- * Sets the shuffle mode to the {@link MediaPlaylistAgent}.
- *
- * @param shuffleMode The shuffle mode
- * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
- * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
- * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
- */
- public void setShuffleMode(@ShuffleMode int shuffleMode) {
- mProvider.setShuffleMode_impl(shuffleMode);
- }
-}
diff --git a/media/java/android/media/Rating2.java b/media/java/android/media/Rating2.java
deleted file mode 100644
index 9213190..0000000
--- a/media/java/android/media/Rating2.java
+++ /dev/null
@@ -1,260 +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.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.IntDef;
-import android.media.update.ApiLoader;
-import android.media.update.Rating2Provider;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * @hide
- * A class to encapsulate rating information used as content metadata.
- * A rating is defined by its rating style (see {@link #RATING_HEART},
- * {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
- * {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may
- * be defined as "unrated"), both of which are defined when the rating instance is constructed
- * through one of the factory methods.
- */
-// New version of Rating with following change
-// - Don't implement Parcelable for updatable support.
-public final class Rating2 {
- /**
- * @hide
- */
- @IntDef({RATING_NONE, RATING_HEART, RATING_THUMB_UP_DOWN, RATING_3_STARS, RATING_4_STARS,
- RATING_5_STARS, RATING_PERCENTAGE})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Style {}
-
- /**
- * @hide
- */
- @IntDef({RATING_3_STARS, RATING_4_STARS, RATING_5_STARS})
- @Retention(RetentionPolicy.SOURCE)
- public @interface StarStyle {}
-
- /**
- * Indicates a rating style is not supported. A Rating2 will never have this
- * type, but can be used by other classes to indicate they do not support
- * Rating2.
- */
- public final static int RATING_NONE = 0;
-
- /**
- * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to
- * indicate the content referred to is a favorite (or not).
- */
- public final static int RATING_HEART = 1;
-
- /**
- * A rating style for "thumb up" vs "thumb down".
- */
- public final static int RATING_THUMB_UP_DOWN = 2;
-
- /**
- * A rating style with 0 to 3 stars.
- */
- public final static int RATING_3_STARS = 3;
-
- /**
- * A rating style with 0 to 4 stars.
- */
- public final static int RATING_4_STARS = 4;
-
- /**
- * A rating style with 0 to 5 stars.
- */
- public final static int RATING_5_STARS = 5;
-
- /**
- * A rating style expressed as a percentage.
- */
- public final static int RATING_PERCENTAGE = 6;
-
- private final Rating2Provider mProvider;
-
- /**
- * @hide
- */
- public Rating2(@NonNull Rating2Provider provider) {
- mProvider = provider;
- }
-
- @Override
- public String toString() {
- return mProvider.toString_impl();
- }
-
- /**
- * @hide
- */
- public Rating2Provider getProvider() {
- return mProvider;
- }
-
- @Override
- public boolean equals(Object obj) {
- return mProvider.equals_impl(obj);
- }
-
- @Override
- public int hashCode() {
- return mProvider.hashCode_impl();
- }
-
- /**
- * Create an instance from bundle object, previoulsy created by {@link #toBundle()}
- *
- * @param bundle bundle
- * @return new Rating2 instance or {@code null} for error
- */
- public static Rating2 fromBundle(@Nullable Bundle bundle) {
- return ApiLoader.getProvider().fromBundle_Rating2(bundle);
- }
-
- /**
- * Return bundle for this object to share across the process.
- * @return bundle of this object
- */
- public Bundle toBundle() {
- return mProvider.toBundle_impl();
- }
-
- /**
- * Return a Rating2 instance with no rating.
- * Create and return a new Rating2 instance with no rating known for the given
- * rating style.
- * @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
- * {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
- * or {@link #RATING_PERCENTAGE}.
- * @return null if an invalid rating style is passed, a new Rating2 instance otherwise.
- */
- public static @Nullable Rating2 newUnratedRating(@Style int ratingStyle) {
- return ApiLoader.getProvider().newUnratedRating_Rating2(ratingStyle);
- }
-
- /**
- * Return a Rating2 instance with a heart-based rating.
- * Create and return a new Rating2 instance with a rating style of {@link #RATING_HEART},
- * and a heart-based rating.
- * @param hasHeart true for a "heart selected" rating, false for "heart unselected".
- * @return a new Rating2 instance.
- */
- public static @Nullable Rating2 newHeartRating(boolean hasHeart) {
- return ApiLoader.getProvider().newHeartRating_Rating2(hasHeart);
- }
-
- /**
- * Return a Rating2 instance with a thumb-based rating.
- * Create and return a new Rating2 instance with a {@link #RATING_THUMB_UP_DOWN}
- * rating style, and a "thumb up" or "thumb down" rating.
- * @param thumbIsUp true for a "thumb up" rating, false for "thumb down".
- * @return a new Rating2 instance.
- */
- public static @Nullable Rating2 newThumbRating(boolean thumbIsUp) {
- return ApiLoader.getProvider().newThumbRating_Rating2(thumbIsUp);
- }
-
- /**
- * Return a Rating2 instance with a star-based rating.
- * Create and return a new Rating2 instance with one of the star-base rating styles
- * and the given integer or fractional number of stars. Non integer values can for instance
- * be used to represent an average rating value, which might not be an integer number of stars.
- * @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
- * {@link #RATING_5_STARS}.
- * @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to
- * the rating style.
- * @return null if the rating style is invalid, or the rating is out of range,
- * a new Rating2 instance otherwise.
- */
- public static @Nullable Rating2 newStarRating(
- @StarStyle int starRatingStyle, float starRating) {
- return ApiLoader.getProvider().newStarRating_Rating2(starRatingStyle, starRating);
- }
-
- /**
- * Return a Rating2 instance with a percentage-based rating.
- * Create and return a new Rating2 instance with a {@link #RATING_PERCENTAGE}
- * rating style, and a rating of the given percentage.
- * @param percent the value of the rating
- * @return null if the rating is out of range, a new Rating2 instance otherwise.
- */
- public static @Nullable Rating2 newPercentageRating(float percent) {
- return ApiLoader.getProvider().newPercentageRating_Rating2(percent);
- }
-
- /**
- * Return whether there is a rating value available.
- * @return true if the instance was not created with {@link #newUnratedRating(int)}.
- */
- public boolean isRated() {
- return mProvider.isRated_impl();
- }
-
- /**
- * Return the rating style.
- * @return one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
- * {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
- * or {@link #RATING_PERCENTAGE}.
- */
- public @Style int getRatingStyle() {
- return mProvider.getRatingStyle_impl();
- }
-
- /**
- * Return whether the rating is "heart selected".
- * @return true if the rating is "heart selected", false if the rating is "heart unselected",
- * if the rating style is not {@link #RATING_HEART} or if it is unrated.
- */
- public boolean hasHeart() {
- return mProvider.hasHeart_impl();
- }
-
- /**
- * Return whether the rating is "thumb up".
- * @return true if the rating is "thumb up", false if the rating is "thumb down",
- * if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated.
- */
- public boolean isThumbUp() {
- return mProvider.isThumbUp_impl();
- }
-
- /**
- * Return the star-based rating value.
- * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
- * not star-based, or if it is unrated.
- */
- public float getStarRating() {
- return mProvider.getStarRating_impl();
- }
-
- /**
- * Return the percentage-based rating value.
- * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
- * not percentage-based, or if it is unrated.
- */
- public float getPercentRating() {
- return mProvider.getPercentRating_impl();
- }
-}
diff --git a/media/java/android/media/SessionCommand2.java b/media/java/android/media/SessionCommand2.java
deleted file mode 100644
index fe86a3a..0000000
--- a/media/java/android/media/SessionCommand2.java
+++ /dev/null
@@ -1,336 +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.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.media.update.ApiLoader;
-import android.media.update.MediaSession2Provider;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
-import android.net.Uri;
-import android.os.Bundle;
-
-import java.util.List;
-
-/**
- * @hide
- * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
- * <p>
- * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
- * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
- * {@link #getCustomCommand()} shouldn't be {@code null}.
- */
-public final class SessionCommand2 {
- /**
- * Command code for the custom command which can be defined by string action in the
- * {@link SessionCommand2}.
- */
- public static final int COMMAND_CODE_CUSTOM = 0;
-
- /**
- * Command code for {@link MediaController2#play()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
- * SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_PLAY = 1;
-
- /**
- * Command code for {@link MediaController2#pause()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
- * SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
-
- /**
- * Command code for {@link MediaController2#stop()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
- * SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
-
- /**
- * Command code for {@link MediaController2#skipToNextItem()}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the {@link SessionCallback#onCommandRequest(
- * MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM = 4;
-
- /**
- * Command code for {@link MediaController2#skipToPreviousItem()}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the {@link SessionCallback#onCommandRequest(
- * MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM = 5;
-
- /**
- * Command code for {@link MediaController2#prepare()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
- * SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
-
- /**
- * Command code for {@link MediaController2#fastForward()}.
- */
- public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 7;
-
- /**
- * Command code for {@link MediaController2#rewind()}.
- */
- public static final int COMMAND_CODE_SESSION_REWIND = 8;
-
- /**
- * Command code for {@link MediaController2#seekTo(long)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
- * SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
-
- /**
- * Command code for both {@link MediaController2#setVolumeTo(int, int)}.
- * <p>
- * Command would set the device volume or send to the volume provider directly if the session
- * doesn't reject the request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_SET_VOLUME = 10;
-
- /**
- * Command code for both {@link MediaController2#adjustVolume(int, int)}.
- * <p>
- * Command would adjust the device volume or send to the volume provider directly if the session
- * doesn't reject the request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_ADJUST_VOLUME = 11;
-
- /**
- * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12;
-
- /**
- * Command code for {@link MediaController2#setShuffleMode(int)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13;
-
- /**
- * Command code for {@link MediaController2#setRepeatMode(int)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14;
-
- /**
- * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15;
-
- /**
- * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16;
-
- /**
- * Command code for {@link MediaController2#replacePlaylistItem(int, MediaItem2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17;
-
- /**
- * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
- * information to the controller.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18;
-
- /**
- * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19;
-
- /**
- * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose
- * metadata information to the controller.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20;
-
- /**
- * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21;
-
- /**
- * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID = 22;
-
- /**
- * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PLAY_FROM_URI = 23;
-
- /**
- * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PLAY_FROM_SEARCH = 24;
-
- /**
- * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID = 25;
-
- /**
- * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PREPARE_FROM_URI = 26;
-
- /**
- * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH = 27;
-
- /**
- * Command code for {@link MediaController2#setRating(String, Rating2)}.
- */
- public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
-
- // TODO(jaewan): Add javadoc
- public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;
- public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 30;
- public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 31;
- public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 32;
- public static final int COMMAND_CODE_LIBRARY_SEARCH = 33;
- public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 34;
- public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 35;
-
- // TODO(jaewan): Rename and move provider
- private final MediaSession2Provider.CommandProvider mProvider;
-
- public SessionCommand2(int commandCode) {
- mProvider = ApiLoader.getProvider().createMediaSession2Command(
- this, commandCode, null, null);
- }
-
- public SessionCommand2(@NonNull String action, @Nullable Bundle extras) {
- if (action == null) {
- throw new IllegalArgumentException("action shouldn't be null");
- }
- mProvider = ApiLoader.getProvider().createMediaSession2Command(
- this, COMMAND_CODE_CUSTOM, action, extras);
- }
-
- /**
- * @hide
- */
- public MediaSession2Provider.CommandProvider getProvider() {
- return mProvider;
- }
-
- public int getCommandCode() {
- return mProvider.getCommandCode_impl();
- }
-
- public @Nullable String getCustomCommand() {
- return mProvider.getCustomCommand_impl();
- }
-
- public @Nullable Bundle getExtras() {
- return mProvider.getExtras_impl();
- }
-
- /**
- * @return a new Bundle instance from the Command
- * @hide
- */
- public Bundle toBundle() {
- return mProvider.toBundle_impl();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof SessionCommand2)) {
- return false;
- }
- return mProvider.equals_impl(((SessionCommand2) obj).mProvider);
- }
-
- @Override
- public int hashCode() {
- return mProvider.hashCode_impl();
- }
-
- /**
- * @return a new Command instance from the Bundle
- * @hide
- */
- public static SessionCommand2 fromBundle(@NonNull Bundle command) {
- return ApiLoader.getProvider().fromBundle_MediaSession2Command(command);
- }
-}
diff --git a/media/java/android/media/SessionCommandGroup2.java b/media/java/android/media/SessionCommandGroup2.java
deleted file mode 100644
index 399765e..0000000
--- a/media/java/android/media/SessionCommandGroup2.java
+++ /dev/null
@@ -1,106 +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.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.media.update.ApiLoader;
-import android.media.update.MediaSession2Provider;
-import android.os.Bundle;
-
-import java.util.Set;
-
-/**
- * @hide
- * Represent set of {@link SessionCommand2}.
- */
-public final class SessionCommandGroup2 {
- // TODO(jaewan): Rename and move provider
- private final MediaSession2Provider.CommandGroupProvider mProvider;
-
- public SessionCommandGroup2() {
- mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, null);
- }
-
- public SessionCommandGroup2(@Nullable SessionCommandGroup2 others) {
- mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, others);
- }
-
- /**
- * @hide
- */
- public SessionCommandGroup2(@NonNull MediaSession2Provider.CommandGroupProvider provider) {
- mProvider = provider;
- }
-
- public void addCommand(@NonNull SessionCommand2 command) {
- mProvider.addCommand_impl(command);
- }
-
- public void addCommand(int commandCode) {
- // TODO(jaewna): Implement
- }
-
- public void addAllPredefinedCommands() {
- mProvider.addAllPredefinedCommands_impl();
- }
-
- public void removeCommand(@NonNull SessionCommand2 command) {
- mProvider.removeCommand_impl(command);
- }
-
- public void removeCommand(int commandCode) {
- // TODO(jaewan): Implement.
- }
-
- public boolean hasCommand(@NonNull SessionCommand2 command) {
- return mProvider.hasCommand_impl(command);
- }
-
- public boolean hasCommand(int code) {
- return mProvider.hasCommand_impl(code);
- }
-
- public @NonNull
- Set<SessionCommand2> getCommands() {
- return mProvider.getCommands_impl();
- }
-
- /**
- * @hide
- */
- public @NonNull MediaSession2Provider.CommandGroupProvider getProvider() {
- return mProvider;
- }
-
- /**
- * @return new bundle from the CommandGroup
- * @hide
- */
- public @NonNull Bundle toBundle() {
- return mProvider.toBundle_impl();
- }
-
- /**
- * @return new instance of CommandGroup from the bundle
- * @hide
- */
- public static @Nullable SessionCommandGroup2 fromBundle(Bundle commands) {
- return ApiLoader.getProvider().fromBundle_MediaSession2CommandGroup(commands);
- }
-}
diff --git a/media/java/android/media/SessionToken2.java b/media/java/android/media/SessionToken2.java
deleted file mode 100644
index f7d54f2..0000000
--- a/media/java/android/media/SessionToken2.java
+++ /dev/null
@@ -1,164 +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.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.content.Context;
-import android.media.session.MediaSessionManager;
-import android.media.update.ApiLoader;
-import android.media.update.SessionToken2Provider;
-import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * @hide
- * Represents an ongoing {@link MediaSession2}.
- * If it's representing a session service, it may not be ongoing.
- * <p>
- * This may be passed to apps by the session owner to allow them to create a
- * {@link MediaController2} to communicate with the session.
- * <p>
- * It can be also obtained by {@link MediaSessionManager}.
- */
-// New version of MediaSession.Token for following reasons
-// - Stop implementing Parcelable for updatable support
-// - Represent session and library service (formerly browser service) in one class.
-// Previously MediaSession.Token was for session and ComponentName was for service.
-public final class SessionToken2 {
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
- public @interface TokenType {
- }
-
- public static final int TYPE_SESSION = 0;
- public static final int TYPE_SESSION_SERVICE = 1;
- public static final int TYPE_LIBRARY_SERVICE = 2;
-
- private final SessionToken2Provider mProvider;
-
- // From the return value of android.os.Process.getUidForName(String) when error
- private static final int UID_UNKNOWN = -1;
-
- /**
- * Constructor for the token. You can only create token for session service or library service
- * to use by {@link MediaController2} or {@link MediaBrowser2}.
- *
- * @param context context
- * @param packageName package name
- * @param serviceName name of service. Can be {@code null} if it's not an service.
- */
- public SessionToken2(@NonNull Context context, @NonNull String packageName,
- @NonNull String serviceName) {
- this(context, packageName, serviceName, UID_UNKNOWN);
- }
-
- /**
- * Constructor for the token. You can only create token for session service or library service
- * to use by {@link MediaController2} or {@link MediaBrowser2}.
- *
- * @param context context
- * @param packageName package name
- * @param serviceName name of service. Can be {@code null} if it's not an service.
- * @param uid uid of the app.
- * @hide
- */
- public SessionToken2(@NonNull Context context, @NonNull String packageName,
- @NonNull String serviceName, int uid) {
- mProvider = ApiLoader.getProvider().createSessionToken2(
- context, this, packageName, serviceName, uid);
- }
-
- /**
- * Constructor for the token.
- * @hide
- */
- public SessionToken2(@NonNull SessionToken2Provider provider) {
- mProvider = provider;
- }
-
- @Override
- public int hashCode() {
- return mProvider.hashCode_impl();
- }
-
- @Override
- public boolean equals(Object obj) {
- return mProvider.equals_impl(obj);
- }
-
- @Override
- public String toString() {
- return mProvider.toString_impl();
- }
-
- /**
- * @hide
- */
- public SessionToken2Provider getProvider() {
- return mProvider;
- }
-
- /**
- * @return uid of the session
- */
- public int getUid() {
- return mProvider.getUid_impl();
- }
-
- /**
- * @return package name
- */
- public String getPackageName() {
- return mProvider.getPackageName_impl();
- }
-
- /**
- * @return id
- */
- public String getId() {
- return mProvider.getId_imp();
- }
-
- /**
- * @return type of the token
- * @see #TYPE_SESSION
- * @see #TYPE_SESSION_SERVICE
- */
- public @TokenType int getType() {
- return mProvider.getType_impl();
- }
-
- /**
- * Create a token from the bundle, exported by {@link #toBundle()}.
- * @param bundle
- * @return
- */
- public static SessionToken2 fromBundle(@NonNull Bundle bundle) {
- return ApiLoader.getProvider().fromBundle_SessionToken2(bundle);
- }
-
- /**
- * Create a {@link Bundle} from this token to share it across processes.
- * @return Bundle
- */
- public Bundle toBundle() {
- return mProvider.toBundle_impl();
- }
-}
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/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
deleted file mode 100644
index 1a4608f..0000000
--- a/media/java/android/media/VolumeProvider2.java
+++ /dev/null
@@ -1,147 +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.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.media.update.ApiLoader;
-import android.media.update.VolumeProvider2Provider;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * @hide
- * Handles requests to adjust or set the volume on a session. This is also used
- * to push volume updates back to the session. The provider must call
- * {@link #setCurrentVolume(int)} each time the volume being provided changes.
- * <p>
- * You can set a volume provider on a session by calling
- * {@link MediaSession2#updatePlayer}.
- */
-// New version of VolumeProvider with following changes
-// - Don't implement Parcelable for updatable support.
-public abstract class VolumeProvider2 {
- /**
- * @hide
- */
- @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE})
- @Retention(RetentionPolicy.SOURCE)
- public @interface ControlType {}
-
- /**
- * The volume is fixed and can not be modified. Requests to change volume
- * should be ignored.
- */
- public static final int VOLUME_CONTROL_FIXED = 0;
-
- /**
- * The volume control uses relative adjustment via
- * {@link #onAdjustVolume(int)}. Attempts to set the volume to a specific
- * value should be ignored.
- */
- public static final int VOLUME_CONTROL_RELATIVE = 1;
-
- /**
- * The volume control uses an absolute value. It may be adjusted using
- * {@link #onAdjustVolume(int)} or set directly using
- * {@link #onSetVolumeTo(int)}.
- */
- public static final int VOLUME_CONTROL_ABSOLUTE = 2;
-
- private final VolumeProvider2Provider mProvider;
-
- /**
- * Create a new volume provider for handling volume events. You must specify
- * the type of volume control, the maximum volume that can be used, and the
- * current volume on the output.
- *
- * @param controlType The method for controlling volume that is used by this provider.
- * @param maxVolume The maximum allowed volume.
- * @param currentVolume The current volume on the output.
- */
- public VolumeProvider2(@ControlType int controlType, int maxVolume, int currentVolume) {
- mProvider = ApiLoader.getProvider().createVolumeProvider2(
- this, controlType, maxVolume, currentVolume);
- }
-
- /**
- * @hide
- */
- public VolumeProvider2Provider getProvider() {
- return mProvider;
- }
-
- /**
- * Get the volume control type that this volume provider uses.
- *
- * @return The volume control type for this volume provider
- */
- @ControlType
- public final int getControlType() {
- return mProvider.getControlType_impl();
- }
-
- /**
- * Get the maximum volume this provider allows.
- *
- * @return The max allowed volume.
- */
- public final int getMaxVolume() {
- return mProvider.getMaxVolume_impl();
- }
-
- /**
- * Gets the current volume. This will be the last value set by
- * {@link #setCurrentVolume(int)}.
- *
- * @return The current volume.
- */
- public final int getCurrentVolume() {
- return mProvider.getCurrentVolume_impl();
- }
-
- /**
- * Notify the system that the current volume has been changed. This must be
- * called every time the volume changes to ensure it is displayed properly.
- *
- * @param currentVolume The current volume on the output.
- */
- public final void setCurrentVolume(int currentVolume) {
- mProvider.setCurrentVolume_impl(currentVolume);
- }
-
- /**
- * Override to handle requests to set the volume of the current output.
- * After the volume has been modified {@link #setCurrentVolume} must be
- * called to notify the system.
- *
- * @param volume The volume to set the output to.
- */
- public void onSetVolumeTo(int volume) { }
-
- /**
- * Override to handle requests to adjust the volume of the current output.
- * Direction will be one of {@link AudioManager#ADJUST_LOWER},
- * {@link AudioManager#ADJUST_RAISE}, {@link AudioManager#ADJUST_SAME}.
- * After the volume has been modified {@link #setCurrentVolume} must be
- * called to notify the system.
- *
- * @param direction The direction to change the volume in.
- */
- public void onAdjustVolume(int direction) { }
-}
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 3578c16..4ced7be 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -17,7 +17,6 @@
import android.content.ComponentName;
import android.media.IRemoteVolumeController;
-import android.media.ISessionTokensListener;
import android.media.session.IActiveSessionsListener;
import android.media.session.ICallback;
import android.media.session.IOnMediaKeyListener;
@@ -55,12 +54,4 @@
// MediaSession2
boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
- boolean createSession2(in Bundle sessionToken);
- void destroySession2(in Bundle sessionToken);
- List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly,
- String packageName);
-
- void addSessionTokensListener(in ISessionTokensListener listener, int userId,
- String packageName);
- void removeSessionTokensListener(in ISessionTokensListener listener, String packageName);
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 98f3fb2..8215779 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -26,11 +26,7 @@
import android.content.Context;
import android.media.AudioManager;
import android.media.IRemoteVolumeController;
-import android.media.ISessionTokensListener;
-import android.media.MediaSession2;
-import android.media.SessionToken2;
import android.media.browse.MediaBrowser;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -44,10 +40,8 @@
import android.view.KeyEvent;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.Executor;
/**
* Provides support for interacting with {@link MediaSession media sessions}
@@ -75,8 +69,6 @@
private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners
= new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>();
- private final ArrayMap<OnSessionTokensChangedListener, SessionTokensChangedWrapper>
- mSessionTokensListener = new ArrayMap<>();
private final Object mLock = new Object();
private final ISessionManager mService;
@@ -413,73 +405,6 @@
}
/**
- * Called when a {@link MediaSession2} is created.
- * @hide
- */
- public boolean createSession2(@NonNull SessionToken2 token) {
- if (token == null) {
- return false;
- }
- try {
- return mService.createSession2(token.toBundle());
- } catch (RemoteException e) {
- Log.wtf(TAG, "Cannot communicate with the service.", e);
- }
- return false;
- }
-
- /**
- * Called when a {@link MediaSession2} is destroyed.
- * @hide
- */
- public void destroySession2(@NonNull SessionToken2 token) {
- if (token == null) {
- return;
- }
- try {
- mService.destroySession2(token.toBundle());
- } catch (RemoteException e) {
- Log.wtf(TAG, "Cannot communicate with the service.", e);
- }
- }
-
- /**
- * @hide
- * Get {@link List} of {@link SessionToken2} whose sessions are active now. This list represents
- * active sessions regardless of whether they're {@link MediaSession2}.
- * <p>
- * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
- * calling app. You may also retrieve this list if your app is an enabled notification listener
- * using the {@link NotificationListenerService} APIs.
- *
- * @return list of tokens
- */
- public List<SessionToken2> getActiveSessionTokens() {
- try {
- List<Bundle> bundles = mService.getSessionTokens(
- /* activeSessionOnly */ true, /* sessionServiceOnly */ false,
- mContext.getOpPackageName());
- return toTokenList(bundles);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Cannot communicate with the service.", e);
- return Collections.emptyList();
- }
- }
-
- private static List<SessionToken2> toTokenList(List<Bundle> bundles) {
- List<SessionToken2> tokens = new ArrayList<>();
- if (bundles != null) {
- for (int i = 0; i < bundles.size(); i++) {
- SessionToken2 token = SessionToken2.fromBundle(bundles.get(i));
- if (token != null) {
- tokens.add(token);
- }
- }
- }
- return tokens;
- }
-
- /**
* Check if the global priority session is currently active. This can be
* used to decide if media keys should be sent to the session or to the app.
*
@@ -601,15 +526,6 @@
}
/**
- * @hide
- * Listens for changes to the {@link #getAllSessionTokens()}. This can be added
- * using {@link #addOnActiveSessionsChangedListener}.
- */
- public interface OnSessionTokensChangedListener {
- void onSessionTokensChanged(@NonNull List<SessionToken2> tokens);
- }
-
- /**
* Listens the volume key long-presses.
* @hide
*/
@@ -815,41 +731,6 @@
}
}
- private static final class SessionTokensChangedWrapper {
- private Context mContext;
- private Executor mExecutor;
- private OnSessionTokensChangedListener mListener;
-
- public SessionTokensChangedWrapper(Context context, Executor executor,
- OnSessionTokensChangedListener listener) {
- mContext = context;
- mExecutor = executor;
- mListener = listener;
- }
-
- private final ISessionTokensListener.Stub mStub = new ISessionTokensListener.Stub() {
- @Override
- public void onSessionTokensChanged(final List<Bundle> bundles) {
- final Executor executor = mExecutor;
- if (executor != null) {
- executor.execute(() -> {
- final Context context = mContext;
- final OnSessionTokensChangedListener listener = mListener;
- if (context != null && listener != null) {
- listener.onSessionTokensChanged(toTokenList(bundles));
- }
- });
- }
- }
- };
-
- private void release() {
- mListener = null;
- mContext = null;
- mExecutor = null;
- }
- }
-
private static final class OnVolumeKeyLongPressListenerImpl
extends IOnVolumeKeyLongPressListener.Stub {
private OnVolumeKeyLongPressListener mListener;
diff --git a/media/java/android/media/update/ApiLoader.java b/media/java/android/media/update/ApiLoader.java
deleted file mode 100644
index 0c1d1a2..0000000
--- a/media/java/android/media/update/ApiLoader.java
+++ /dev/null
@@ -1,79 +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 android.media.update;
-
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Build;
-import android.os.RemoteException;
-import android.os.UserHandle;
-
-import com.android.internal.annotations.GuardedBy;
-
-import dalvik.system.PathClassLoader;
-
-import java.io.File;
-
-/**
- * @hide
- */
-public final class ApiLoader {
- @GuardedBy("this")
- private static StaticProvider sMediaUpdatable;
-
- private static final String UPDATE_PACKAGE = "com.android.media.update";
- private static final String UPDATE_CLASS = "com.android.media.update.ApiFactory";
- private static final String UPDATE_METHOD = "initialize";
- private static final boolean REGISTER_UPDATE_DEPENDENCY = true;
-
- private ApiLoader() { }
-
- public static StaticProvider getProvider() {
- try {
- return getMediaUpdatable();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (NameNotFoundException | ReflectiveOperationException e) {
- throw new RuntimeException(e);
- }
- }
-
- // TODO This method may do I/O; Ensure it does not violate (emit warnings in) strict mode.
- private static synchronized StaticProvider getMediaUpdatable()
- throws NameNotFoundException, ReflectiveOperationException, RemoteException {
- if (sMediaUpdatable != null) return sMediaUpdatable;
-
- // TODO Figure out when to use which package (query media update service)
- int flags = Build.IS_DEBUGGABLE ? 0 : PackageManager.MATCH_SYSTEM_ONLY;
- ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
- UPDATE_PACKAGE, flags, UserHandle.myUserId());
-
- if (REGISTER_UPDATE_DEPENDENCY) {
- // Register a dependency to the updatable in order to be killed during updates
- ActivityManager.getService().addPackageDependency(ai.packageName);
- }
-
- ClassLoader classLoader = new PathClassLoader(ai.sourceDir,
- ai.nativeLibraryDir + File.pathSeparator + System.getProperty("java.library.path"),
- ClassLoader.getSystemClassLoader().getParent());
- return sMediaUpdatable = (StaticProvider) classLoader.loadClass(UPDATE_CLASS)
- .getMethod(UPDATE_METHOD, ApplicationInfo.class).invoke(null, ai);
- }
-}
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
deleted file mode 100644
index 7234f7b..0000000
--- a/media/java/android/media/update/MediaController2Provider.java
+++ /dev/null
@@ -1,82 +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.media.update;
-
-import android.app.PendingIntent;
-import android.media.AudioAttributes;
-import android.media.MediaController2.PlaybackInfo;
-import android.media.MediaItem2;
-import android.media.MediaMetadata2;
-import android.media.SessionCommand2;
-import android.media.Rating2;
-import android.media.SessionToken2;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-import java.util.List;
-
-/**
- * @hide
- */
-public interface MediaController2Provider extends TransportControlProvider {
- void initialize();
-
- void close_impl();
- SessionToken2 getSessionToken_impl();
- boolean isConnected_impl();
-
- PendingIntent getSessionActivity_impl();
-
- void setVolumeTo_impl(int value, int flags);
- void adjustVolume_impl(int direction, int flags);
- PlaybackInfo getPlaybackInfo_impl();
-
- void prepareFromUri_impl(Uri uri, Bundle extras);
- void prepareFromSearch_impl(String query, Bundle extras);
- void prepareFromMediaId_impl(String mediaId, Bundle extras);
- void playFromSearch_impl(String query, Bundle extras);
- void playFromUri_impl(Uri uri, Bundle extras);
- void playFromMediaId_impl(String mediaId, Bundle extras);
- void fastForward_impl();
- void rewind_impl();
-
- void setRating_impl(String mediaId, Rating2 rating);
- void sendCustomCommand_impl(SessionCommand2 command, Bundle args, ResultReceiver cb);
- List<MediaItem2> getPlaylist_impl();
- void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
- MediaMetadata2 getPlaylistMetadata_impl();
- void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
-
- void addPlaylistItem_impl(int index, MediaItem2 item);
- void replacePlaylistItem_impl(int index, MediaItem2 item);
- void removePlaylistItem_impl(MediaItem2 item);
-
- int getPlayerState_impl();
- long getCurrentPosition_impl();
- float getPlaybackSpeed_impl();
- long getBufferedPosition_impl();
- MediaItem2 getCurrentMediaItem_impl();
-
- interface PlaybackInfoProvider {
- int getPlaybackType_impl();
- AudioAttributes getAudioAttributes_impl();
- int getControlType_impl();
- int getMaxVolume_impl();
- int getCurrentVolume_impl();
- }
-}
diff --git a/media/java/android/media/update/MediaItem2Provider.java b/media/java/android/media/update/MediaItem2Provider.java
deleted file mode 100644
index 47db22f..0000000
--- a/media/java/android/media/update/MediaItem2Provider.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.media.update;
-
-import android.media.DataSourceDesc;
-import android.media.MediaItem2;
-import android.media.MediaItem2.Builder;
-import android.media.MediaMetadata2;
-import android.os.Bundle;
-
-/**
- * @hide
- */
-public interface MediaItem2Provider {
- Bundle toBundle_impl();
- String toString_impl();
- int getFlags_impl();
- boolean isBrowsable_impl();
- boolean isPlayable_impl();
- void setMetadata_impl(MediaMetadata2 metadata);
- MediaMetadata2 getMetadata_impl();
- String getMediaId_impl();
- DataSourceDesc getDataSourceDesc_impl();
- boolean equals_impl(Object obj);
-
- interface BuilderProvider {
- Builder setMediaId_impl(String mediaId);
- Builder setMetadata_impl(MediaMetadata2 metadata);
- Builder setDataSourceDesc_impl(DataSourceDesc dataSourceDesc);
- MediaItem2 build_impl();
- }
-}
diff --git a/media/java/android/media/update/MediaMetadata2Provider.java b/media/java/android/media/update/MediaMetadata2Provider.java
deleted file mode 100644
index 22463e9..0000000
--- a/media/java/android/media/update/MediaMetadata2Provider.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package android.media.update;
-
-import android.graphics.Bitmap;
-import android.media.MediaMetadata2;
-import android.media.MediaMetadata2.Builder;
-import android.media.Rating2;
-import android.os.Bundle;
-
-import java.util.Set;
-
-/**
- * @hide
- */
-public interface MediaMetadata2Provider {
- boolean containsKey_impl(String key);
- CharSequence getText_impl(String key);
- String getMediaId_impl();
- String getString_impl(String key);
- long getLong_impl(String key);
- Rating2 getRating_impl(String key);
- Bundle toBundle_impl();
- Set<String> keySet_impl();
- int size_impl();
- Bitmap getBitmap_impl(String key);
- float getFloat_impl(String key);
- Bundle getExtras_impl();
-
- interface BuilderProvider {
- Builder putText_impl(String key, CharSequence value);
- Builder putString_impl(String key, String value);
- Builder putLong_impl(String key, long value);
- Builder putRating_impl(String key, Rating2 value);
- Builder putBitmap_impl(String key, Bitmap value);
- Builder putFloat_impl(String key, float value);
- Builder setExtras_impl(Bundle bundle);
- MediaMetadata2 build_impl();
- }
-}
diff --git a/media/java/android/media/update/MediaPlaylistAgentProvider.java b/media/java/android/media/update/MediaPlaylistAgentProvider.java
deleted file mode 100644
index e1522cf..0000000
--- a/media/java/android/media/update/MediaPlaylistAgentProvider.java
+++ /dev/null
@@ -1,55 +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.media.update;
-
-import android.media.DataSourceDesc;
-import android.media.MediaItem2;
-import android.media.MediaMetadata2;
-import android.media.MediaPlaylistAgent.PlaylistEventCallback;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- */
-public interface MediaPlaylistAgentProvider {
- // final methods of MediaPlaylistAgent
- void registerPlaylistEventCallback_impl(Executor executor, PlaylistEventCallback callback);
- void unregisterPlaylistEventCallback_impl(PlaylistEventCallback callback);
- void notifyPlaylistChanged_impl();
- void notifyPlaylistMetadataChanged_impl();
- void notifyShuffleModeChanged_impl();
- void notifyRepeatModeChanged_impl();
-
- // public methods of MediaPlaylistAgent
- List<MediaItem2> getPlaylist_impl();
- void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
- MediaMetadata2 getPlaylistMetadata_impl();
- void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
- void addPlaylistItem_impl(int index, MediaItem2 item);
- void removePlaylistItem_impl(MediaItem2 item);
- void replacePlaylistItem_impl(int index, MediaItem2 item);
- void skipToPlaylistItem_impl(MediaItem2 item);
- void skipToPreviousItem_impl();
- void skipToNextItem_impl();
- int getRepeatMode_impl();
- void setRepeatMode_impl(int repeatMode);
- int getShuffleMode_impl();
- void setShuffleMode_impl(int shuffleMode);
- MediaItem2 getMediaItem_impl(DataSourceDesc dsd);
-}
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
deleted file mode 100644
index 4751348..0000000
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ /dev/null
@@ -1,132 +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.media.update;
-
-import android.app.PendingIntent;
-import android.media.AudioFocusRequest;
-import android.media.MediaItem2;
-import android.media.MediaMetadata2;
-import android.media.MediaPlayerBase;
-import android.media.MediaPlaylistAgent;
-import android.media.MediaSession2;
-import android.media.SessionCommand2;
-import android.media.MediaSession2.CommandButton;
-import android.media.MediaSession2.CommandButton.Builder;
-import android.media.SessionCommandGroup2;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.OnDataSourceMissingHelper;
-import android.media.MediaSession2.SessionCallback;
-import android.media.SessionToken2;
-import android.media.VolumeProvider2;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- */
-public interface MediaSession2Provider extends TransportControlProvider {
- void close_impl();
- void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistAgent playlistAgent,
- VolumeProvider2 volumeProvider);
- MediaPlayerBase getPlayer_impl();
- MediaMetadata2 getPlaylistMetadata_impl();
- void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
- MediaPlaylistAgent getPlaylistAgent_impl();
- VolumeProvider2 getVolumeProvider_impl();
- SessionToken2 getToken_impl();
- List<ControllerInfo> getConnectedControllers_impl();
- void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout);
- void setAudioFocusRequest_impl(AudioFocusRequest afr);
- void setAllowedCommands_impl(ControllerInfo controller, SessionCommandGroup2 commands);
- void sendCustomCommand_impl(ControllerInfo controller, SessionCommand2 command, Bundle args,
- ResultReceiver receiver);
- void sendCustomCommand_impl(SessionCommand2 command, Bundle args);
- void addPlaylistItem_impl(int index, MediaItem2 item);
- void removePlaylistItem_impl(MediaItem2 item);
- void replacePlaylistItem_impl(int index, MediaItem2 item);
- List<MediaItem2> getPlaylist_impl();
- void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
- MediaItem2 getCurrentPlaylistItem_impl();
- void notifyError_impl(int errorCode, Bundle extras);
- int getPlayerState_impl();
- long getCurrentPosition_impl();
- long getBufferedPosition_impl();
- void setOnDataSourceMissingHelper_impl(OnDataSourceMissingHelper helper);
- void clearOnDataSourceMissingHelper_impl();
-
- // TODO(jaewan): Rename and move provider
- interface CommandProvider {
- int getCommandCode_impl();
- String getCustomCommand_impl();
- Bundle getExtras_impl();
- Bundle toBundle_impl();
-
- boolean equals_impl(Object ob);
- int hashCode_impl();
- }
-
- // TODO(jaewan): Rename and move provider
- interface CommandGroupProvider {
- void addCommand_impl(SessionCommand2 command);
- void addAllPredefinedCommands_impl();
- void removeCommand_impl(SessionCommand2 command);
- boolean hasCommand_impl(SessionCommand2 command);
- boolean hasCommand_impl(int code);
- Set<SessionCommand2> getCommands_impl();
- Bundle toBundle_impl();
- }
-
- interface CommandButtonProvider {
- SessionCommand2 getCommand_impl();
- int getIconResId_impl();
- String getDisplayName_impl();
- Bundle getExtras_impl();
- boolean isEnabled_impl();
-
- interface BuilderProvider {
- Builder setCommand_impl(SessionCommand2 command);
- Builder setIconResId_impl(int resId);
- Builder setDisplayName_impl(String displayName);
- Builder setEnabled_impl(boolean enabled);
- Builder setExtras_impl(Bundle extras);
- CommandButton build_impl();
- }
- }
-
- interface ControllerInfoProvider {
- String getPackageName_impl();
- int getUid_impl();
- boolean isTrusted_impl();
- int hashCode_impl();
- boolean equals_impl(Object obj);
- String toString_impl();
- }
-
- interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> {
- void setPlayer_impl(MediaPlayerBase player);
- void setPlaylistAgent_impl(MediaPlaylistAgent playlistAgent);
- void setVolumeProvider_impl(VolumeProvider2 volumeProvider);
- void setSessionActivity_impl(PendingIntent pi);
- void setId_impl(String id);
- void setSessionCallback_impl(Executor executor, C callback);
- T build_impl();
- }
-}
diff --git a/media/java/android/media/update/Rating2Provider.java b/media/java/android/media/update/Rating2Provider.java
deleted file mode 100644
index 28ad273..0000000
--- a/media/java/android/media/update/Rating2Provider.java
+++ /dev/null
@@ -1,36 +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.media.update;
-
-import android.annotation.SystemApi;
-import android.os.Bundle;
-
-/**
- * @hide
- */
-public interface Rating2Provider {
- String toString_impl();
- boolean equals_impl(Object obj);
- int hashCode_impl();
- Bundle toBundle_impl();
- boolean isRated_impl();
- int getRatingStyle_impl();
- boolean hasHeart_impl();
- boolean isThumbUp_impl();
- float getStarRating_impl();
- float getPercentRating_impl();
-}
diff --git a/media/java/android/media/update/SessionToken2Provider.java b/media/java/android/media/update/SessionToken2Provider.java
deleted file mode 100644
index 95d6ce0..0000000
--- a/media/java/android/media/update/SessionToken2Provider.java
+++ /dev/null
@@ -1,34 +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.media.update;
-
-import android.os.Bundle;
-
-/**
- * @hide
- */
-public interface SessionToken2Provider {
- String getPackageName_impl();
- String getId_imp();
- int getType_impl();
- int getUid_impl();
- Bundle toBundle_impl();
-
- int hashCode_impl();
- boolean equals_impl(Object obj);
- String toString_impl();
-}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
deleted file mode 100644
index ccb8c02..0000000
--- a/media/java/android/media/update/StaticProvider.java
+++ /dev/null
@@ -1,92 +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 android.media.update;
-
-import android.content.Context;
-import android.media.MediaController2;
-import android.media.MediaController2.ControllerCallback;
-import android.media.MediaItem2;
-import android.media.MediaMetadata2;
-import android.media.MediaPlaylistAgent;
-import android.media.MediaSession2;
-import android.media.MediaSession2.SessionCallback;
-import android.media.Rating2;
-import android.media.SessionCommand2;
-import android.media.SessionCommandGroup2;
-import android.media.SessionToken2;
-import android.media.VolumeProvider2;
-import android.media.update.MediaSession2Provider.BuilderBaseProvider;
-import android.media.update.MediaSession2Provider.CommandButtonProvider;
-import android.media.update.MediaSession2Provider.CommandGroupProvider;
-import android.media.update.MediaSession2Provider.CommandProvider;
-import android.media.update.MediaSession2Provider.ControllerInfoProvider;
-import android.os.Bundle;
-import android.os.IInterface;
-
-import java.util.concurrent.Executor;
-
-/**
- * Interface for connecting the public API to an updatable implementation.
- *
- * This interface provides access to constructors and static methods that are otherwise not directly
- * accessible via an implementation object.
- * @hide
- */
-public interface StaticProvider {
- CommandProvider createMediaSession2Command(SessionCommand2 instance,
- int commandCode, String action, Bundle extra);
- SessionCommand2 fromBundle_MediaSession2Command(Bundle bundle);
- CommandGroupProvider createMediaSession2CommandGroup(SessionCommandGroup2 instance,
- SessionCommandGroup2 others);
- SessionCommandGroup2 fromBundle_MediaSession2CommandGroup(Bundle bundle);
- ControllerInfoProvider createMediaSession2ControllerInfo(Context context,
- MediaSession2.ControllerInfo instance, int uid, int pid,
- String packageName, IInterface callback);
- CommandButtonProvider.BuilderProvider createMediaSession2CommandButtonBuilder(
- MediaSession2.CommandButton.Builder instance);
- BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
- Context context, MediaSession2.Builder instance);
-
- MediaController2Provider createMediaController2(Context context, MediaController2 instance,
- SessionToken2 token, Executor executor, ControllerCallback callback);
-
- SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
- String packageName, String serviceName, int uid);
- SessionToken2 fromBundle_SessionToken2(Bundle bundle);
-
- MediaItem2Provider.BuilderProvider createMediaItem2Builder(MediaItem2.Builder instance,
- int flags);
- MediaItem2 fromBundle_MediaItem2(Bundle bundle);
-
- VolumeProvider2Provider createVolumeProvider2(VolumeProvider2 instance, int controlType,
- int maxVolume, int currentVolume);
-
- MediaMetadata2 fromBundle_MediaMetadata2(Bundle bundle);
- MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
- MediaMetadata2.Builder instance);
- MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
- MediaMetadata2.Builder instance, MediaMetadata2 source);
-
- Rating2 newUnratedRating_Rating2(int ratingStyle);
- Rating2 fromBundle_Rating2(Bundle bundle);
- Rating2 newHeartRating_Rating2(boolean hasHeart);
- Rating2 newThumbRating_Rating2(boolean thumbIsUp);
- Rating2 newStarRating_Rating2(int starRatingStyle, float starRating);
- Rating2 newPercentageRating_Rating2(float percent);
-
- MediaPlaylistAgentProvider createMediaPlaylistAgent(MediaPlaylistAgent instance);
-}
diff --git a/media/java/android/media/update/TransportControlProvider.java b/media/java/android/media/update/TransportControlProvider.java
deleted file mode 100644
index d89a88a..0000000
--- a/media/java/android/media/update/TransportControlProvider.java
+++ /dev/null
@@ -1,39 +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.media.update;
-
-import android.media.MediaItem2;
-
-/**
- * @hide
- */
-public interface TransportControlProvider {
- void play_impl();
- void pause_impl();
- void stop_impl();
- void skipToPreviousItem_impl();
- void skipToNextItem_impl();
-
- void prepare_impl();
- void seekTo_impl(long pos);
- void skipToPlaylistItem_impl(MediaItem2 item);
-
- int getRepeatMode_impl();
- void setRepeatMode_impl(int repeatMode);
- int getShuffleMode_impl();
- void setShuffleMode_impl(int shuffleMode);
-}
diff --git a/media/java/android/media/update/VolumeProvider2Provider.java b/media/java/android/media/update/VolumeProvider2Provider.java
deleted file mode 100644
index 5b5cfd3..0000000
--- a/media/java/android/media/update/VolumeProvider2Provider.java
+++ /dev/null
@@ -1,26 +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.media.update;
-
-/**
- * @hide
- */
-public interface VolumeProvider2Provider {
- int getControlType_impl();
- int getMaxVolume_impl();
- int getCurrentVolume_impl();
- void setCurrentVolume_impl(int currentVolume);
-}
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_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
index 5306de6..a3adddd 100644
--- a/media/jni/android_media_AudioPresentation.h
+++ b/media/jni/android_media_AudioPresentation.h
@@ -14,173 +14,135 @@
* limitations under the License.
*/
-#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
-#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+#ifndef _ANDROID_MEDIA_AUDIOPRESENTATION_H_
+#define _ANDROID_MEDIA_AUDIOPRESENTATION_H_
#include "jni.h"
-#include <media/AudioPresentationInfo.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-
+#include <media/stagefright/foundation/ADebug.h> // CHECK
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <nativehelper/ScopedLocalRef.h>
namespace android {
struct JAudioPresentationInfo {
struct fields_t {
- jclass clazz;
+ jclass clazz = NULL;
jmethodID constructID;
// list parameters
- jclass listclazz;
+ jclass listClazz = NULL;
jmethodID listConstructId;
jmethodID listAddId;
+ // hashmap parameters
+ jclass hashMapClazz = NULL;
+ jmethodID hashMapConstructID;
+ jmethodID hashMapPutID;
+
+ // ulocale parameters
+ jclass ulocaleClazz = NULL;
+ jmethodID ulocaleConstructID;
+
void init(JNIEnv *env) {
jclass lclazz = env->FindClass("android/media/AudioPresentation");
- if (lclazz == NULL) {
- return;
- }
-
+ CHECK(lclazz != NULL);
clazz = (jclass)env->NewGlobalRef(lclazz);
- if (clazz == NULL) {
- return;
- }
-
+ CHECK(clazz != NULL);
constructID = env->GetMethodID(clazz, "<init>",
- "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
- env->DeleteLocalRef(lclazz);
+ "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
+ CHECK(constructID != NULL);
// list objects
- jclass llistclazz = env->FindClass("java/util/ArrayList");
- CHECK(llistclazz != NULL);
- listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz));
- CHECK(listclazz != NULL);
- listConstructId = env->GetMethodID(listclazz, "<init>", "()V");
+ jclass llistClazz = env->FindClass("java/util/ArrayList");
+ CHECK(llistClazz != NULL);
+ listClazz = static_cast<jclass>(env->NewGlobalRef(llistClazz));
+ CHECK(listClazz != NULL);
+ listConstructId = env->GetMethodID(listClazz, "<init>", "()V");
CHECK(listConstructId != NULL);
- listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z");
+ listAddId = env->GetMethodID(listClazz, "add", "(Ljava/lang/Object;)Z");
CHECK(listAddId != NULL);
- env->DeleteLocalRef(llistclazz);
+
+ // hashmap objects
+ jclass lhashMapClazz = env->FindClass("java/util/HashMap");
+ CHECK(lhashMapClazz != NULL);
+ hashMapClazz = (jclass)env->NewGlobalRef(lhashMapClazz);
+ CHECK(hashMapClazz != NULL);
+ hashMapConstructID = env->GetMethodID(hashMapClazz, "<init>", "()V");
+ CHECK(hashMapConstructID != NULL);
+ hashMapPutID = env->GetMethodID(
+ hashMapClazz,
+ "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ CHECK(hashMapPutID != NULL);
+
+ jclass lulocaleClazz = env->FindClass("android/icu/util/ULocale");
+ CHECK(lulocaleClazz != NULL);
+ ulocaleClazz = (jclass)env->NewGlobalRef(lulocaleClazz);
+ CHECK(ulocaleClazz != NULL);
+ ulocaleConstructID = env->GetMethodID(ulocaleClazz, "<init>", "(Ljava/lang/String;)V");
+ CHECK(ulocaleConstructID != NULL);
}
void exit(JNIEnv *env) {
- env->DeleteGlobalRef(clazz);
- clazz = NULL;
- env->DeleteGlobalRef(listclazz);
- listclazz = NULL;
+ env->DeleteGlobalRef(clazz); clazz = NULL;
+ env->DeleteGlobalRef(listClazz); listClazz = NULL;
+ env->DeleteGlobalRef(hashMapClazz); hashMapClazz = NULL;
+ env->DeleteGlobalRef(ulocaleClazz); ulocaleClazz = NULL;
}
};
- static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
- ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));
-
- if (hashMapClazz.get() == NULL) {
- return -EINVAL;
- }
- jmethodID hashMapConstructID =
- env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
-
- if (hashMapConstructID == NULL) {
- return -EINVAL;
- }
- jmethodID hashMapPutID =
- env->GetMethodID(
- hashMapClazz.get(),
- "put",
- "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-
- if (hashMapPutID == NULL) {
- return -EINVAL;
- }
-
- jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
-
- for (size_t i = 0; i < msg->countEntries(); ++i) {
- AMessage::Type valueType;
- const char *key = msg->getEntryNameAt(i, &valueType);
-
- if (!strncmp(key, "android._", 9)) {
- // don't expose private keys (starting with android._)
- continue;
- }
- jobject valueObj = NULL;
- AString val;
- CHECK(msg->findString(key, &val));
- valueObj = env->NewStringUTF(val.c_str());
- if (valueObj != NULL) {
- ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
- if (localeClazz.get() == NULL) {
- return -EINVAL;
- }
- jmethodID localeConstructID =
- env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
- if (localeConstructID == NULL) {
- return -EINVAL;
- }
- jstring jLanguage = env->NewStringUTF(key);
- jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
- env->CallObjectMethod(hashMap, hashMapPutID, jLocale, valueObj);
- env->DeleteLocalRef(jLocale); jLocale = NULL;
- env->DeleteLocalRef(valueObj); valueObj = NULL;
- env->DeleteLocalRef(jLanguage); jLanguage = NULL;
- }
- }
-
- *map = hashMap;
-
- return OK;
+ static jobject asJobject(JNIEnv *env, const fields_t& fields) {
+ return env->NewObject(fields.listClazz, fields.listConstructId);
}
- jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) {
- jobject list = env->NewObject(fields.listclazz, fields.listConstructId);
-
- for (size_t i = 0; i < info.countPresentations(); ++i) {
- const sp<AudioPresentation> &ap = info.getPresentation(i);
- jobject jLabelObject;
-
- sp<AMessage> labelMessage = new AMessage();
- for (size_t i = 0; i < ap->mLabels.size(); ++i) {
- labelMessage->setString(ap->mLabels.keyAt(i).string(),
- ap->mLabels.valueAt(i).string());
+ static void addPresentations(JNIEnv *env, const fields_t& fields,
+ const AudioPresentationCollection& presentations, jobject presentationsJObj) {
+ for (const auto& ap : presentations) {
+ ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels);
+ if (jLabelObject == nullptr) return;
+ ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(ap.mLanguage.c_str()));
+ if (jLanguage == nullptr) return;
+ ScopedLocalRef<jobject> jLocale(env, env->NewObject(
+ fields.ulocaleClazz, fields.ulocaleConstructID, jLanguage.get()));
+ ScopedLocalRef<jobject> jValueObj(env, env->NewObject(fields.clazz, fields.constructID,
+ static_cast<jint>(ap.mPresentationId),
+ static_cast<jint>(ap.mProgramId),
+ jLocale.get(),
+ static_cast<jint>(ap.mMasteringIndication),
+ static_cast<jboolean>((ap.mAudioDescriptionAvailable == 1) ? 1 : 0),
+ static_cast<jboolean>((ap.mSpokenSubtitlesAvailable == 1) ? 1 : 0),
+ static_cast<jboolean>((ap.mDialogueEnhancementAvailable == 1) ? 1 : 0),
+ jLabelObject.get()));
+ if (jValueObj != nullptr) {
+ env->CallBooleanMethod(presentationsJObj, fields.listAddId, jValueObj.get());
}
- if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
- return NULL;
- }
- ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
- if (localeClazz.get() == NULL) {
- return NULL;
- }
- jmethodID localeConstructID =
- env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
- if (localeConstructID == NULL) {
- return NULL;
- }
- jstring jLanguage = env->NewStringUTF(ap->mLanguage.c_str());
- jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
- jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
- static_cast<jint>(ap->mPresentationId),
- static_cast<jint>(ap->mProgramId),
- jLocale,
- static_cast<jint>(ap->mMasteringIndication),
- static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
- 1 : 0),
- static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
- 1 : 0),
- static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
- 1 : 0),
- jLabelObject);
- if (jValueObj == NULL) {
- env->DeleteLocalRef(jLanguage); jLanguage = NULL;
- return NULL;
- }
-
- env->CallBooleanMethod(list, fields.listAddId, jValueObj);
- env->DeleteLocalRef(jLocale); jLocale = NULL;
- env->DeleteLocalRef(jValueObj); jValueObj = NULL;
- env->DeleteLocalRef(jLanguage); jLanguage = NULL;
}
- return list;
+ }
+
+ private:
+ static ScopedLocalRef<jobject> convertLabelsToMap(
+ JNIEnv *env, const fields_t& fields, const std::map<std::string, std::string> &labels) {
+ ScopedLocalRef<jobject> nullMap(env, nullptr);
+ ScopedLocalRef<jobject> hashMap(env, env->NewObject(
+ fields.hashMapClazz, fields.hashMapConstructID));
+ if (hashMap == nullptr) {
+ return nullMap;
+ }
+
+ for (const auto& label : labels) {
+ ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(label.first.c_str()));
+ if (jLanguage == nullptr) return nullMap;
+ ScopedLocalRef<jobject> jLocale(env, env->NewObject(
+ fields.ulocaleClazz,
+ fields.ulocaleConstructID,
+ jLanguage.get()));
+ if (jLocale == nullptr) return nullMap;
+ ScopedLocalRef<jobject> jValue(env, env->NewStringUTF(label.second.c_str()));
+ if (jValue == nullptr) return nullMap;
+ env->CallObjectMethod(hashMap.get(), fields.hashMapPutID, jLocale.get(), jValue.get());
+ }
+ return hashMap;
}
};
} // namespace android
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_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 15957c6..29238d3 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "MediaExtractor-JNI"
#include <utils/Log.h>
+#include "android_media_AudioPresentation.h"
#include "android_media_MediaDataSource.h"
#include "android_media_MediaExtractor.h"
#include "android_media_MediaMetricsJNI.h"
@@ -56,6 +57,7 @@
};
static fields_t gFields;
+static JAudioPresentationInfo::fields_t gAudioPresentationFields;
JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
: mClass(NULL),
@@ -289,6 +291,10 @@
return mImpl->getCachedDuration(durationUs, eos);
}
+status_t JMediaExtractor::getAudioPresentations(size_t trackIdx,
+ AudioPresentationCollection *presentations) const {
+ return mImpl->getAudioPresentations(trackIdx, presentations);
+}
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -668,6 +674,28 @@
return JNI_TRUE;
}
+static jobject android_media_MediaExtractor_getAudioPresentations(
+ JNIEnv *env, jobject thiz, jint trackIdx) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+ jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields);
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return presentationsJObj;
+ }
+ AudioPresentationCollection presentations;
+ status_t err = extractor->getAudioPresentations(trackIdx, &presentations);
+ if (err == ERROR_END_OF_STREAM || err == ERROR_UNSUPPORTED) {
+ return presentationsJObj;
+ } else if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return presentationsJObj;
+ }
+
+ JAudioPresentationInfo::addPresentations(
+ env, gAudioPresentationFields, presentations, presentationsJObj);
+ return presentationsJObj;
+}
+
static void android_media_MediaExtractor_native_init(JNIEnv *env) {
jclass clazz = env->FindClass("android/media/MediaExtractor");
CHECK(clazz != NULL);
@@ -683,6 +711,8 @@
gFields.cryptoInfoSetPatternID =
env->GetMethodID(clazz, "setPattern", "(II)V");
+
+ gAudioPresentationFields.init(env);
}
static void android_media_MediaExtractor_native_setup(
@@ -963,6 +993,9 @@
{"native_getMetrics", "()Landroid/os/PersistableBundle;",
(void *)android_media_MediaExtractor_native_getMetrics},
+
+ { "native_getAudioPresentations", "(I)Ljava/util/List;",
+ (void *)android_media_MediaExtractor_getAudioPresentations },
};
int register_android_media_MediaExtractor(JNIEnv *env) {
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index aaa8421..baa779c 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -18,6 +18,7 @@
#define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_
#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <media/MediaSource.h>
#include <media/DataSource.h>
#include <utils/Errors.h>
@@ -66,6 +67,8 @@
status_t getMetrics(Parcel *reply) const;
bool getCachedDuration(int64_t *durationUs, bool *eos) const;
+ status_t getAudioPresentations(size_t trackIdx,
+ AudioPresentationCollection *presentations) const;
protected:
virtual ~JMediaExtractor();
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_functor.cpp b/native/webview/plat_support/draw_gl_functor.cpp
index 7cb49da..e3e52b1 100644
--- a/native/webview/plat_support/draw_gl_functor.cpp
+++ b/native/webview/plat_support/draw_gl_functor.cpp
@@ -42,10 +42,10 @@
class DrawGLFunctor : public Functor {
public:
explicit DrawGLFunctor(jlong view_context) : view_context_(view_context) {}
- virtual ~DrawGLFunctor() {}
+ ~DrawGLFunctor() override {}
// Functor
- virtual status_t operator ()(int what, void* data) {
+ status_t operator ()(int what, void* data) override {
using uirenderer::DrawGlInfo;
if (!g_aw_drawgl_function) {
ALOGE("Cannot draw: no DrawGL Function installed");
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 8f13497..f244f9f 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -45,6 +45,7 @@
"androidx.slice_slice-builders",
"androidx.arch.core_core-runtime",
"androidx.lifecycle_lifecycle-extensions",
+ "car-theme-lib-bp",
"SystemUI-tags",
"SystemUI-proto",
],
diff --git a/packages/CarSystemUI/res/layout/car_volume_dialog.xml b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
index c98740e..709797d 100644
--- a/packages/CarSystemUI/res/layout/car_volume_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
@@ -20,11 +20,9 @@
android:id="@+id/volume_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@android:color/black"
android:minWidth="@dimen/volume_dialog_panel_width"
- android:theme="@style/Theme.Car.DialogListView"
- app:dividerEndMargin="@dimen/car_keyline_1"
- app:dividerStartMargin="@dimen/car_keyline_1"
+ android:theme="@style/PagedListViewTheme"
app:gutter="none"
app:scrollBarEnabled="false"
+ app:listDividerColor="@color/list_divider_color"
app:showPagedListViewDivider="true"/>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index df8f8db..c510ab6 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -51,4 +51,6 @@
<color name="car_grey_900">#ff212121</color>
<color name="keyguard_button_text_color">@android:color/black</color>
+
+ <color name="list_divider_color">@*android:color/car_list_divider_light</color>
</resources>
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 7f4544a..0d95d30 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -41,15 +41,16 @@
<item name="android:colorControlHighlight">@color/nav_bar_ripple_background_color</item>
</style>
- <style name="Theme.Car.DialogListView" parent="@style/Theme.Car.NoActionBar">
- <item name="android:colorControlActivated">@color/car_accent</item>
- <item name="listItemBackgroundColor">@android:color/black</item>
- </style>
-
<style name="NavigationBarButton">
<item name="android:layout_height">96dp</item>
<item name="android:layout_width">96dp</item>
<item name="android:background">@drawable/nav_button_background</item>
</style>
-</resources>
+ <style name="PagedListViewTheme" parent="@style/Theme.CarSupportWrapper.NoActionBar">
+ <item name="android:background">@*android:color/car_background</item>
+ <item name="listItemBackgroundColor">@*android:color/car_background</item>
+ <item name="dividerEndMargin">@dimen/car_keyline_1</item>
+ <item name="dividerStartMargin">@dimen/car_keyline_1</item>
+ </style>
+</resources>
\ 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 60153fc..4b212c2 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -355,6 +355,37 @@
}
@Override
+ public void onNotificationExpansionChanged(String key, boolean isUserAction,
+ boolean isExpanded) {
+ if (DEBUG) {
+ Log.i(TAG,
+ "onNotificationExpansionChanged " + key + ", isUserAction =" + isUserAction
+ + ", isExpanded = isExpanded");
+ }
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
+ }
+
+ @Override
+ public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+ if (DEBUG) {
+ Log.d(TAG, "onSuggestedReplySent() called with: key = [" + key + "], reply = [" + reply
+ + "], source = [" + source + "]");
+ }
+ }
+
+ @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/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 56feb47..87d6e4a 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -16,27 +16,24 @@
package com.android.location.fused;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.ProviderPropertiesUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Criteria;
-import android.location.LocationProvider;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.os.WorkSource;
+import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.ProviderPropertiesUnbundled;
+import com.android.location.provider.ProviderRequestUnbundled;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
private static final String TAG = "FusedLocationProvider";
@@ -48,7 +45,6 @@
private static final int MSG_DISABLE = 2;
private static final int MSG_SET_REQUEST = 3;
- private final Context mContext;
private final FusionEngine mEngine;
private static class RequestWrapper {
@@ -62,13 +58,12 @@
public FusedLocationProvider(Context context) {
super(TAG, PROPERTIES);
- mContext = context;
mEngine = new FusionEngine(context, Looper.myLooper());
// listen for user change
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ context.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -122,14 +117,4 @@
// perform synchronously
mEngine.dump(fd, pw, args);
}
-
- @Override
- public int onGetStatus(Bundle extras) {
- return LocationProvider.AVAILABLE;
- }
-
- @Override
- public long onGetStatusUpdateTime() {
- return 0;
- }
}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index d1f140f..444e724 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -16,6 +16,7 @@
"SettingsLibAppPreference",
"SettingsLibSearchWidget",
"SettingsLibSettingsSpinner",
+ "SettingsLayoutPreference",
],
// 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/Android.bp b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
new file mode 100644
index 0000000..489d360
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLayoutPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml b/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/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/SettingsLayoutPreference/res/layout/layout_preference_frame.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
new file mode 100644
index 0000000..ee4ce49
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
new file mode 100644
index 0000000..789d185
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
@@ -0,0 +1,105 @@
+<?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.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/entity_header"
+ style="@style/EntityHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/entity_header_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/entity_header_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:scaleType="fitXY"
+ android:antialias="true"/>
+
+ <TextView
+ android:id="@+id/entity_header_title"
+ style="@style/TextAppearance.EntityHeaderTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:ellipsize="marquee"
+ android:textDirection="locale"
+ android:layout_marginTop="8dp"/>
+
+ <TextView
+ android:id="@+id/install_type"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"/>
+
+ <TextView
+ android:id="@+id/entity_header_summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"/>
+
+ <TextView
+ android:id="@+id/entity_header_second_summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_alignParentEnd="true"
+ android:orientation="vertical">
+
+ <ImageButton
+ android:id="@android:id/button1"
+ style="?android:attr/actionOverflowButtonStyle"
+ 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"/>
+
+ <ImageButton
+ android:id="@android:id/button2"
+ style="?android:attr/actionOverflowButtonStyle"
+ 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"/>
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
new file mode 100644
index 0000000..805744b
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
@@ -0,0 +1,42 @@
+<?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="EntityHeader">
+ <item name="android:background">?android:attr/colorPrimaryDark</item>
+ <item name="android:paddingTop">24dp</item>
+ <item name="android:paddingBottom">16dp</item>
+ <item name="android:paddingEnd">16dp</item>
+ </style>
+
+ <style name="TextAppearance.EntityHeaderTitle"
+ parent="@android:style/TextAppearance.Material.Subhead">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">20sp</item>
+ </style>
+
+ <style name="TextAppearance.EntityHeaderSummary"
+ parent="@android:style/TextAppearance.Material.Body1">
+ <item name="android:textAlignment">viewStart</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:gravity">start</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
new file mode 100644
index 0000000..2a635b0
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
@@ -0,0 +1,174 @@
+/*
+ * 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.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.core.content.res.TypedArrayUtils;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * A preference can be simply customized a view by adding layout attribute in xml.
+ * User also can decide whether or not LayoutPreference allows above divider or below divider.
+ *
+ * For instances,
+ *
+ * <com.android.settingslib.widget.LayoutPreference
+ * ...
+ * android:layout="@layout/settings_entity_header"
+ * xxxxxxx:allowDividerAbove="true"
+ * xxxxxxx:allowDividerBelow="true"
+ *
+ */
+public class LayoutPreference extends Preference {
+
+ private final View.OnClickListener mClickListener = v -> performClick(v);
+ private boolean mAllowDividerAbove;
+ private boolean mAllowDividerBelow;
+ private View mRootView;
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme and the supplied
+ * attribute set.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ */
+ public LayoutPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, 0 /* defStyleAttr */);
+ }
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme, the supplied
+ * attribute set, and default style attribute.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ * @param defStyleAttr An attribute in the current theme that contains a
+ * reference to a style resource that supplies default
+ * values for the view. Can be 0 to not look for
+ * defaults.
+ */
+ public LayoutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context, attrs, defStyleAttr);
+ }
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme and a customized view id.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param resource The view id which you expected to be inflated and show in preference.
+ */
+ public LayoutPreference(Context context, int resource) {
+ this(context, LayoutInflater.from(context).inflate(resource, null, false));
+ }
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme and a customized view.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param view The view which you expected show in preference.
+ */
+ public LayoutPreference(Context context, View view) {
+ super(context);
+ setView(view);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Preference);
+ mAllowDividerAbove = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerAbove,
+ R.styleable.Preference_allowDividerAbove, false);
+ mAllowDividerBelow = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerBelow,
+ R.styleable.Preference_allowDividerBelow, false);
+ a.recycle();
+
+ a = context.obtainStyledAttributes(
+ attrs, R.styleable.Preference, defStyleAttr, 0);
+ int layoutResource = a.getResourceId(R.styleable.Preference_android_layout, 0);
+ if (layoutResource == 0) {
+ throw new IllegalArgumentException("LayoutPreference requires a layout to be defined");
+ }
+ a.recycle();
+
+ // Need to create view now so that findViewById can be called immediately.
+ final View view = LayoutInflater.from(getContext())
+ .inflate(layoutResource, null, false);
+ setView(view);
+ }
+
+ private void setView(View view) {
+ setLayoutResource(R.layout.layout_preference_frame);
+ mRootView = view;
+ setShouldDisableView(false);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ holder.itemView.setOnClickListener(mClickListener);
+
+ final boolean selectable = isSelectable();
+ holder.itemView.setFocusable(selectable);
+ holder.itemView.setClickable(selectable);
+ holder.setDividerAllowedAbove(mAllowDividerAbove);
+ holder.setDividerAllowedBelow(mAllowDividerBelow);
+
+ FrameLayout layout = (FrameLayout) holder.itemView;
+ layout.removeAllViews();
+ ViewGroup parent = (ViewGroup) mRootView.getParent();
+ if (parent != null) {
+ parent.removeView(mRootView);
+ }
+ layout.addView(mRootView);
+ }
+
+ /**
+ * Finds the view with the given ID.
+ *
+ * @param id the ID to search for
+ * @return a view with given ID if found, or {@code null} otherwise
+ */
+ public <T extends View> T findViewById(int id) {
+ return mRootView.findViewById(id);
+ }
+
+ /**
+ * LayoutPreference whether or not allows to set a below divider.
+ */
+ public void setAllowDividerBelow(boolean allowed) {
+ mAllowDividerBelow = allowed;
+ }
+
+ /**
+ * Return a value whether or not LayoutPreference allows to set a below divider.
+ */
+ public boolean isAllowDividerBelow() {
+ return mAllowDividerBelow;
+ }
+}
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..a97e054 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
@@ -210,32 +215,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 +252,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 +525,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 +544,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 +568,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 +632,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 +664,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 +843,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 +865,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 +948,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/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 8fac3fd..99d48d3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -34,7 +34,7 @@
import com.android.settingslib.R;
-import libcore.util.TimeZoneFinder;
+import libcore.timezone.TimeZoneFinder;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 86928fc..4f81daf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -154,8 +154,10 @@
static final String KEY_CARRIER_NAME = "key_carrier_name";
static final AtomicInteger sLastId = new AtomicInteger(0);
- /**
- * These values are matched in string arrays -- changes must be kept in sync
+ /*
+ * NOTE: These constants for security and PSK types are saved to the bundle in saveWifiState,
+ * and sent across IPC. The numeric values should remain stable, otherwise the changes will need
+ * to be synced with other unbundled users of this library.
*/
public static final int SECURITY_NONE = 0;
public static final int SECURITY_WEP = 1;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index ede248b..8757eed 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -55,6 +55,8 @@
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/"));
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 47e51f3..08a75ab 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -22,12 +22,11 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -54,7 +53,6 @@
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowAudioManager;
import org.robolectric.shadows.ShadowSettings;
import java.util.HashMap;
@@ -72,7 +70,7 @@
private static final String PERCENTAGE_50 = "50%";
private static final String PERCENTAGE_100 = "100%";
- private ShadowAudioManager mShadowAudioManager;
+ private AudioManager mAudioManager;
private Context mContext;
@Mock
private LocationManager mLocationManager;
@@ -85,7 +83,7 @@
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
ShadowSecure.reset();
- mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
+ mAudioManager = mContext.getSystemService(AudioManager.class);
}
@Test
@@ -205,28 +203,28 @@
@Test
public void isAudioModeOngoingCall_modeInCommunication_returnTrue() {
- mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
}
@Test
public void isAudioModeOngoingCall_modeInCall_returnTrue() {
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
}
@Test
public void isAudioModeOngoingCall_modeRingtone_returnTrue() {
- mShadowAudioManager.setMode(AudioManager.MODE_RINGTONE);
+ mAudioManager.setMode(AudioManager.MODE_RINGTONE);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
}
@Test
public void isAudioModeOngoingCall_modeNormal_returnFalse() {
- mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isFalse();
}
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 49520c0..5ceede1 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
@@ -17,15 +17,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
@@ -40,7 +39,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowAudioManager;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class CachedBluetoothDeviceTest {
@@ -67,7 +65,7 @@
@Mock
private BluetoothDevice mSubDevice;
private CachedBluetoothDevice mCachedDevice;
- private ShadowAudioManager mShadowAudioManager;
+ private AudioManager mAudioManager;
private Context mContext;
private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
@@ -75,7 +73,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
+ mAudioManager = mContext.getSystemService(AudioManager.class);
when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
@@ -212,7 +210,7 @@
// 2. Audio Manager: In Call
updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
// Act & Assert:
// Get null result without Battery Level.
@@ -228,7 +226,7 @@
updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
mBatteryLevel = 10;
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
// Act & Assert:
// Get "10% battery" result with Battery Level 10.
@@ -244,14 +242,13 @@
// Set device as Active for HFP and test connection state summary
mCachedDevice.onAudioModeChanged();
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Test with battery level
mBatteryLevel = 10;
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
- "Active, 10% battery");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active, 10% battery");
// Set HFP profile to be disconnected and test connection state summary
updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -363,7 +360,7 @@
// 2. Audio Manager: In Call
updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
// Act & Assert:
// Get "Active" result without Battery Level.
@@ -378,8 +375,8 @@
// 3. Audio Manager: In Call
updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
mBatteryLevel = 10;
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
// Act & Assert:
// Get "Active, 10% battery" result with Battery Level 10.
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
new file mode 100644
index 0000000..427a611
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.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.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+
+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.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class LayoutPreferenceTest {
+
+ private LayoutPreference mPreference;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ final Context mContext = RuntimeEnvironment.application;
+ mPreference = new LayoutPreference(mContext, R.layout.settings_entity_header);
+ mHolder = PreferenceViewHolder.createInstanceForTests(LayoutInflater.from(mContext)
+ .inflate(R.layout.layout_preference_frame, null, false));
+ }
+
+ @Test
+ public void setOnPreferenceClickListener_layoutPreferenceShouldListenClickEvent() {
+ final OnPreferenceClickListener listener = mock(OnPreferenceClickListener.class);
+
+ mPreference.setOnPreferenceClickListener(listener);
+ mPreference.onBindViewHolder(mHolder);
+
+ mHolder.itemView.callOnClick();
+
+ verify(listener).onPreferenceClick(mPreference);
+ assertThat(mHolder.itemView.isFocusable()).isTrue();
+ assertThat(mHolder.itemView.isClickable()).isTrue();
+ }
+
+ @Test
+ public void setNonSelectable_viewShouldNotBeSelectable() {
+ mPreference.setSelectable(false);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.itemView.isFocusable()).isFalse();
+ assertThat(mHolder.itemView.isClickable()).isFalse();
+ }
+
+ @Test
+ public void disableSomeView_shouldMaintainStateAfterBind() {
+ mPreference.findViewById(android.R.id.button1).setEnabled(false);
+ mPreference.findViewById(android.R.id.button2).setEnabled(true);
+
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mPreference.findViewById(android.R.id.button1).isEnabled()).isFalse();
+ assertThat(mPreference.findViewById(android.R.id.button2).isEnabled()).isTrue();
+ }
+
+ @Test
+ public void allowDividerBelow_shouldSaveCorrectDividerStatus() {
+ mPreference.setAllowDividerBelow(true);
+
+ assertThat(mPreference.isAllowDividerBelow()).isTrue();
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index cbb6e82..b2c12b2 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,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index e0c4d72..00ea45c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -185,6 +185,9 @@
private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
Settings.NameValueTable.VALUE, null);
+ public static final String RESULT_ROWS_DELETED = "result_rows_deleted";
+ public static final String RESULT_SETTINGS_LIST = "result_settings_list";
+
// Overlay specified settings whitelisted for Instant Apps
private static final Set<String> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS = new ArraySet<>();
private static final Set<String> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS = new ArraySet<>();
@@ -460,6 +463,48 @@
break;
}
+ case Settings.CALL_METHOD_DELETE_SYSTEM: {
+ int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
+ Bundle result = new Bundle();
+ result.putInt(RESULT_ROWS_DELETED, rows);
+ return result;
+ }
+
+ case Settings.CALL_METHOD_DELETE_SECURE: {
+ int rows = deleteSecureSetting(name, requestingUserId, false) ? 1 : 0;
+ Bundle result = new Bundle();
+ result.putInt(RESULT_ROWS_DELETED, rows);
+ return result;
+ }
+
+ case Settings.CALL_METHOD_DELETE_GLOBAL: {
+ int rows = deleteGlobalSetting(name, requestingUserId, false) ? 1 : 0;
+ Bundle result = new Bundle();
+ result.putInt(RESULT_ROWS_DELETED, rows);
+ return result;
+ }
+
+ case Settings.CALL_METHOD_LIST_SYSTEM: {
+ Bundle result = new Bundle();
+ result.putStringArrayList(RESULT_SETTINGS_LIST,
+ buildSettingsList(getAllSystemSettings(requestingUserId, null)));
+ return result;
+ }
+
+ case Settings.CALL_METHOD_LIST_SECURE: {
+ Bundle result = new Bundle();
+ result.putStringArrayList(RESULT_SETTINGS_LIST,
+ buildSettingsList(getAllSecureSettings(requestingUserId, null)));
+ return result;
+ }
+
+ case Settings.CALL_METHOD_LIST_GLOBAL: {
+ Bundle result = new Bundle();
+ result.putStringArrayList(RESULT_SETTINGS_LIST,
+ buildSettingsList(getAllGlobalSettings(null)));
+ return result;
+ }
+
default: {
Slog.w(LOG_TAG, "call() with invalid method: " + method);
} break;
@@ -529,6 +574,20 @@
}
}
+ private ArrayList<String> buildSettingsList(Cursor cursor) {
+ final ArrayList<String> lines = new ArrayList<String>();
+ try {
+ while (cursor != null && cursor.moveToNext()) {
+ lines.add(cursor.getString(1) + "=" + cursor.getString(2));
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return lines;
+ }
+
@Override
public Uri insert(Uri uri, ContentValues values) {
if (DEBUG) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 379cfc7..13537c4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -19,8 +19,6 @@
import android.app.ActivityManager;
import android.content.IContentProvider;
import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Process;
@@ -265,9 +263,6 @@
}
if (mUser < 0) {
mUser = UserHandle.USER_SYSTEM;
- } else if (mVerb == CommandVerb.DELETE || mVerb == CommandVerb.LIST) {
- perr.println("--user not supported for delete and list.");
- return -1;
}
UserManager userManager = UserManager.get(mProvider.getContext());
if (userManager.getUserInfo(mUser) == null) {
@@ -304,27 +299,22 @@
return 0;
}
- private List<String> listForUser(IContentProvider provider, int userHandle, String table) {
- final Uri uri = "system".equals(table) ? Settings.System.CONTENT_URI
- : "secure".equals(table) ? Settings.Secure.CONTENT_URI
- : "global".equals(table) ? Settings.Global.CONTENT_URI
- : null;
- final ArrayList<String> lines = new ArrayList<String>();
- if (uri == null) {
- return lines;
+ List<String> listForUser(IContentProvider provider, int userHandle, String table) {
+ final String callListCommand;
+ if ("system".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_SYSTEM;
+ else if ("secure".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_SECURE;
+ else if ("global".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_GLOBAL;
+ else {
+ getErrPrintWriter().println("Invalid table; no list performed");
+ throw new IllegalArgumentException("Invalid table " + table);
}
+ final ArrayList<String> lines = new ArrayList<String>();
try {
- final Cursor cursor = provider.query(resolveCallingPackage(), uri, null, null,
- null);
- try {
- while (cursor != null && cursor.moveToNext()) {
- lines.add(cursor.getString(1) + "=" + cursor.getString(2));
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
+ Bundle arg = new Bundle();
+ arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
+ Bundle result =
+ provider.call(resolveCallingPackage(), callListCommand, null, arg);
+ lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST));
Collections.sort(lines);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
@@ -392,22 +382,27 @@
int deleteForUser(IContentProvider provider, int userHandle,
final String table, final String key) {
- Uri targetUri;
- if ("system".equals(table)) targetUri = Settings.System.getUriFor(key);
- else if ("secure".equals(table)) targetUri = Settings.Secure.getUriFor(key);
- else if ("global".equals(table)) targetUri = Settings.Global.getUriFor(key);
- else {
+ final String callDeleteCommand;
+ if ("system".equals(table)) {
+ callDeleteCommand = Settings.CALL_METHOD_DELETE_SYSTEM;
+ } else if ("secure".equals(table)) {
+ callDeleteCommand = Settings.CALL_METHOD_DELETE_SECURE;
+ } else if ("global".equals(table)) {
+ callDeleteCommand = Settings.CALL_METHOD_DELETE_GLOBAL;
+ } else {
getErrPrintWriter().println("Invalid table; no delete performed");
throw new IllegalArgumentException("Invalid table " + table);
}
- int num = 0;
try {
- num = provider.delete(resolveCallingPackage(), targetUri, null, null);
+ Bundle arg = new Bundle();
+ arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
+ Bundle result =
+ provider.call(resolveCallingPackage(), callDeleteCommand, key, arg);
+ return result.getInt(SettingsProvider.RESULT_ROWS_DELETED);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
}
- return num;
}
void resetForUser(IContentProvider provider, int userHandle,
@@ -473,12 +468,12 @@
pw.println(" Change the contents of KEY to VALUE.");
pw.println(" TAG to associate with the setting.");
pw.println(" {default} to set as the default, case-insensitive only for global/secure namespace");
- pw.println(" delete NAMESPACE KEY");
+ pw.println(" delete [--user <USER_ID> | current] NAMESPACE KEY");
pw.println(" Delete the entry for KEY.");
pw.println(" reset [--user <USER_ID> | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}");
pw.println(" Reset the global/secure table for a package with mode.");
pw.println(" RESET_MODE is one of {untrusted_defaults, untrusted_clear, trusted_defaults}, case-insensitive");
- pw.println(" list NAMESPACE");
+ pw.println(" list [--user <USER_ID> | current] NAMESPACE");
pw.println(" Print all defined keys.");
pw.println(" NAMESPACE is one of {system, secure, global}, case-insensitive");
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index 572a924..183f599 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -32,9 +32,11 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
-import java.util.concurrent.atomic.AtomicBoolean;
+
import org.junit.Test;
+import java.util.concurrent.atomic.AtomicBoolean;
+
/**
* Tests for the SettingContentProvider.
*
@@ -258,7 +260,7 @@
FAKE_SETTING_VALUE, false);
// Reset the changes made by the "shell/root" package
- resetToDefaultsViaShell(type, "shell");
+ resetToDefaultsViaShell(type, "com.android.shell");
resetToDefaultsViaShell(type, "root");
// Make sure the old APIs don't set defaults
@@ -272,7 +274,7 @@
FAKE_SETTING_VALUE_2, false);
// Reset the changes made by this package
- resetToDefaultsViaShell(type, "shell");
+ resetToDefaultsViaShell(type, "com.android.shell");
resetToDefaultsViaShell(type, "root");
// Make sure the old APIs don't set defaults
@@ -313,7 +315,7 @@
FAKE_SETTING_VALUE_2, "TOKEN2", false);
// Reset settings associated with TOKEN1
- resetToDefaultsViaShell(type, "shell", "TOKEN1");
+ resetToDefaultsViaShell(type, "com.android.shell", "TOKEN1");
resetToDefaultsViaShell(type, "root", "TOKEN1");
// Make sure TOKEN1 settings are reset
@@ -325,7 +327,7 @@
FAKE_SETTING_NAME_1));
// Reset settings associated with TOKEN2
- resetToDefaultsViaShell(type, "shell", "TOKEN2");
+ resetToDefaultsViaShell(type, "com.android.shell", "TOKEN2");
resetToDefaultsViaShell(type, "root", "TOKEN2");
// Make sure TOKEN2 settings are reset
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 75c0391..6ba1fcb 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -1,10 +1,12 @@
+set noparent
+
+jsharkey@android.com
+felipeal@google.com
+nandana@google.com
svetoslavganov@google.com
hackbod@google.com
yamasani@google.com
moltmann@google.com
toddke@google.com
-jsharkey@google.com
cbrubaker@google.com
omakoto@google.com
-nandana@google.com
-felipeal@google.com
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/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index ba4eb5f..88b8dd8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -52,7 +52,9 @@
}
class Sensor {
- public static int TYPE_WAKE_LOCK_SCREEN = 1;
+ public static final int TYPE_WAKE_LOCK_SCREEN = 1;
+ public static final int TYPE_WAKE_DISPLAY = 2;
+ public static final int TYPE_SWIPE = 3;
int mType;
@@ -68,6 +70,7 @@
class TriggerEvent {
Sensor mSensor;
int mVendorType;
+ float[] mValues;
/**
* Creates a trigger event
@@ -76,14 +79,30 @@
* e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
*/
public TriggerEvent(Sensor sensor, int vendorType) {
+ this(sensor, vendorType, null);
+ }
+
+ /**
+ * Creates a trigger event
+ * @param sensor The type of sensor, e.g. TYPE_WAKE_LOCK_SCREEN
+ * @param vendorType The vendor type, which should be unique for each type of sensor,
+ * e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
+ * @param values Values captured by the sensor.
+ */
+ public TriggerEvent(Sensor sensor, int vendorType, float[] values) {
mSensor = sensor;
mVendorType = vendorType;
+ mValues = values;
}
public Sensor getSensor() {
return mSensor;
}
+ public float[] getValues() {
+ return mValues;
+ }
+
public int getVendorType() {
return mVendorType;
}
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index c86ebe7..eb3f70a 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -51,7 +51,7 @@
android:layout_centerVertical="true"
android:layout_toEndOf="@id/pkgicon" />
<TextView
- android:id="@+id/pkg_group_divider"
+ android:id="@+id/pkg_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -61,7 +61,7 @@
android:layout_centerVertical="true"
android:layout_toEndOf="@id/pkgname" />
<TextView
- android:id="@+id/group_name"
+ android:id="@+id/delegate_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -70,7 +70,7 @@
android:ellipsize="end"
android:maxLines="1"
android:layout_centerVertical="true"
- android:layout_toEndOf="@id/pkg_group_divider" />
+ android:layout_toEndOf="@id/pkg_divider" />
<!-- 24 dp icon with 16 dp padding all around to mirror notification content margins -->
<ImageButton
android:id="@+id/info"
@@ -101,13 +101,39 @@
android:layout_marginStart="@*android:dimen/notification_content_margin_start"
android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
android:orientation="vertical">
- <!-- Channel Name -->
- <TextView
- android:id="@+id/channel_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- style="@android:style/TextAppearance.Material.Notification.Title" />
+ <RelativeLayout
+ android:id="@+id/names"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/group_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:layout_centerVertical="true" />
+ <TextView
+ android:id="@+id/pkg_group_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:text="@*android:string/notification_header_divider_symbol"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@id/group_name" />
+ <!-- Channel Name -->
+ <TextView
+ android:id="@+id/channel_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ style="@android:style/TextAppearance.Material.Notification.Title"
+ android:layout_toEndOf="@id/pkg_group_divider"/>
+ </RelativeLayout>
<!-- Question prompt -->
<TextView
android:id="@+id/block_prompt"
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
index 2f7d486..bc15f2c4 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
@@ -26,9 +26,7 @@
android:id="@+id/dialog_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="@dimen/ongoing_appops_dialog_content_padding">
-
+ android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
@@ -37,21 +35,29 @@
android:textDirection="locale"
android:textAppearance="@style/TextAppearance.AppOpsDialog.Title"
android:textColor="@*android:color/text_color_primary"
- android:paddingStart="@dimen/ongoing_appops_dialog_title_padding"
- android:paddingEnd="@dimen/ongoing_appops_dialog_title_padding"
- android:paddingBottom="@dimen/ongoing_appops_dialog_sep"
+ android:layout_marginStart="@dimen/ongoing_appops_dialog_title_margin_sides"
+ android:layout_marginEnd="@dimen/ongoing_appops_dialog_title_margin_sides"
+ android:layout_marginBottom="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
+ android:layout_marginTop="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
/>
<LinearLayout
- android:id="@+id/items_container"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="start"
- />
+ android:layout_marginBottom="@dimen/ongoing_appops_dialog_items_bottom_margin" >
- <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
- android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/items_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="start"
+ />
+
+ <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
+ android:visibility="gone" />
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
index f05f7ba..ecfbfb4 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
@@ -17,37 +17,39 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/ongoing_appops_dialog_line_height"
+ android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
+ android:layout_marginEnd="@dimen/ongoing_appops_dialog_text_padding"
android:fillViewport="true"
android:orientation="horizontal"
- android:layout_marginTop="@dimen/ongoing_appops_dialog_text_margin"
- android:focusable="true" >
+ android:focusable="true"
+ android:layout_gravity="center_vertical">
<ImageView
android:id="@+id/app_icon"
- android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
- android:layout_width="@dimen/ongoing_appops_dialog_icon_height"
+ android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
+ android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
+ android:layout_gravity="start|center_vertical"
/>
<TextView
android:id="@+id/app_name"
- android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+ android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
- android:gravity="bottom|start"
+ android:gravity="start|center_vertical"
android:textDirection="locale"
android:textAppearance="@style/TextAppearance.AppOpsDialog.Item"
android:textColor="@*android:color/text_color_primary"
- android:paddingStart="@dimen/ongoing_appops_dialog_text_padding"
- android:paddingEnd="@dimen/ongoing_appops_dialog_text_padding"
-
+ android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
/>
<LinearLayout
android:id="@+id/icons"
- android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+ android:layout_height="match_parent"
android:layout_width="wrap_content"
android:gravity="end"
+ android:layout_gravity="end|center_vertical"
android:visibility="gone"
/>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/smart_action_button.xml b/packages/SystemUI/res/layout/smart_action_button.xml
new file mode 100644
index 0000000..2716034
--- /dev/null
+++ b/packages/SystemUI/res/layout/smart_action_button.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ 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
+ -->
+
+<!-- android:paddingHorizontal is set dynamically in SmartReplyView. -->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.Button"
+ android:stateListAnimator="@null"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="0dp"
+ android:minHeight="@dimen/smart_reply_button_min_height"
+ android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
+ android:background="@drawable/smart_reply_button_background"
+ android:gravity="center"
+ android:fontFamily="roboto-medium"
+ android:textSize="@dimen/smart_reply_button_font_size"
+ android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
+ android:textColor="@color/smart_reply_button_text"
+ android:drawablePadding="@dimen/smart_action_button_icon_padding"
+ android:textStyle="normal"
+ android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d8648fa..9e97cd8 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -464,4 +464,6 @@
<string-array name="config_pluginWhitelist" translatable="false">
<item>com.android.systemui</item>
</string-array>
+
+ <integer name="ongoing_appops_dialog_max_apps">5</integer>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 07628c6..b0a519c 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_padding">10dp</dimen>
<!-- A reasonable upper bound for the height of the smart reply button. The measuring code
needs to start with a guess for the maximum size. Currently two-line smart reply buttons
@@ -944,14 +945,20 @@
<dimen name="ongoing_appops_dialog_sep">16dp</dimen>
<!--Padding around text items in Ongoing App Ops dialog -->
<dimen name="ongoing_appops_dialog_text_padding">16dp</dimen>
- <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
- <dimen name="ongoing_appops_dialog_icon_height">28dp</dimen>
- <!-- Margin between text lines in Ongoing App Ops dialog -->
- <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
- <!-- Side padding of title in Ongoing App Ops dialog -->
- <dimen name="ongoing_appops_dialog_title_padding">10dp</dimen>
- <!-- Padding around Ongoing App Ops dialog content -->
- <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
+ <!-- Height and width of App Opp icons in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_icon_size">24dp</dimen>
+ <!-- Left margin of App Opp icons in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_icon_margin">8dp</dimen>
+ <!-- Height and width of Application icons in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_app_icon_size">32dp</dimen>
+ <!-- Height of line in Ongoing App Ops dialog-->
+ <dimen name="ongoing_appops_dialog_line_height">48dp</dimen>
+ <!-- Side margin of title in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_title_margin_sides">24dp</dimen>
+ <!-- Bottom margin of items in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_items_bottom_margin">24dp</dimen>
+ <!-- Top and bottom margin of title in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_title_margin_top_bottom">18dp</dimen>
<!-- Side margins around the Ongoing App Ops chip-->
<dimen name="ongoing_appops_chip_margin">12dp</dimen>
<!-- Top and bottom margins around the Ongoing App Ops chip -->
@@ -967,9 +974,9 @@
<!-- Radius of Ongoing App Ops chip corners -->
<dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
<!-- Text size for Ongoing App Ops dialog title -->
- <dimen name="ongoing_appops_dialog_title_size">24sp</dimen>
+ <dimen name="ongoing_appops_dialog_title_size">20sp</dimen>
<!-- Text size for Ongoing App Ops dialog items -->
- <dimen name="ongoing_appops_dialog_item_size">20sp</dimen>
+ <dimen name="ongoing_appops_dialog_item_size">16sp</dimen>
<!-- How much a bubble is elevated -->
<dimen name="bubble_elevation">8dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4a0bc9b..c5654f0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1574,6 +1574,9 @@
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
<string name="notification_unblockable_desc">These notifications can\'t be turned off</string>
+ <!-- Notification: Control panel: Label for the app that posted this notification, if it's not the package that the notification was posted for -->
+ <string name="notification_delegate_header">via <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+
<!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
<string name="appops_camera">This app is using the camera.</string>
<!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
@@ -2251,7 +2254,10 @@
<string name="heap_dump_tile_name">Dump SysUI Heap</string>
<!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
- <string name="ongoing_privacy_chip_multiple_apps"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</string>
+ <plurals name="ongoing_privacy_chip_multiple_apps">
+ <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>
<!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
<string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
@@ -2260,12 +2266,15 @@
<string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
<!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]-->
- <string name="ongoing_privacy_chip_content_multiple_apps_single_op"><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>.</string>
+ <plurals name="ongoing_privacy_chip_content_multiple_apps_single_op">
+ <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>
<!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
<string name="ongoing_privacy_dialog_cancel">Cancel</string>
- <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=15]-->
+ <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
<string name="ongoing_privacy_dialog_open_settings">View details</string>
<!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
@@ -2292,6 +2301,6 @@
<!-- Text for indicating extra apps using app ops [CHAR LIMIT=NONE] -->
<plurals name="ongoing_privacy_dialog_overflow_text">
<item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> other app</item>
- <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other app</item>
+ <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other apps</item>
</plurals>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e9aa1b6..fede934 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -262,7 +262,7 @@
<style name="TextAppearance.AppOpsDialog.Item">
<item name="android:textSize">@dimen/ongoing_appops_dialog_item_size</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:fontFamily">sans-serif</item>
</style>
<style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index c7910f9..46ed715b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -54,7 +54,6 @@
import android.util.Log;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
-
import android.view.RemoteAnimationTarget;
import com.android.internal.app.IVoiceInteractionManagerService;
@@ -480,4 +479,16 @@
return false;
}
}
+
+ /**
+ * Returns true if the system supports freeform multi-window.
+ */
+ public boolean supportsFreeformMultiWindow(Context context) {
+ final boolean freeformDevOption = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+ return ActivityTaskManager.supportsMultiWindow(context)
+ && (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+ || freeformDevOption);
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 7154f53..a6b66e7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -18,6 +18,7 @@
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import android.app.ActivityOptions;
@@ -41,6 +42,15 @@
return options;
}
+ /**
+ * @return ActivityOptions for starting a task in freeform.
+ */
+ public static ActivityOptions makeFreeformOptions() {
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+ return options;
+ }
+
public static ActivityOptions makeRemoteAnimation(
RemoteAnimationAdapterCompat remoteAnimationAdapter) {
return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped());
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 3191d14..ff9e84c 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
@@ -17,6 +17,9 @@
package com.android.systemui.shared.system;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
import android.app.WindowConfiguration;
import android.graphics.Rect;
@@ -26,10 +29,6 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
@@ -101,23 +100,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);
}
@@ -160,11 +159,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;
}
@@ -179,6 +180,7 @@
*/
public int getNavBarPosition() {
try {
+ // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
return WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
} catch (RemoteException e) {
Log.w(TAG, "Failed to get nav bar position");
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 6b0a7a9..b55aa5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1531,10 +1531,11 @@
}
mHandler.removeCallbacks(mRetryFingerprintAuthentication);
boolean shouldListenForFingerprint = shouldListenForFingerprint();
- if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFingerprint) {
+ boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
+ || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
+ if (runningOrRestarting && !shouldListenForFingerprint) {
stopListeningForFingerprint();
- } else if (mFingerprintRunningState != BIOMETRIC_STATE_RUNNING
- && shouldListenForFingerprint) {
+ } else if (!runningOrRestarting && shouldListenForFingerprint) {
startListeningForFingerprint();
}
}
@@ -1589,6 +1590,10 @@
setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
return;
}
+ if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ // Waiting for restart via handleFingerprintError().
+ return;
+ }
if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
int userId = ActivityManager.getCurrentUser();
if (isUnlockWithFingerprintPossible(userId)) {
@@ -2418,6 +2423,8 @@
+ getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
pw.println(" disabled(DPM)=" + isFingerprintDisabled(userId));
pw.println(" possible=" + isUnlockWithFingerprintPossible(userId));
+ pw.println(" listening: actual=" + mFingerprintRunningState
+ + " expected=" + (shouldListenForFingerprint() ? 1 : 0));
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
}
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/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e868f96..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;
@@ -268,7 +273,7 @@
for (BubbleView bv : mBubbles.values()) {
NotificationData.Entry entry = bv.getEntry();
if (entry != null) {
- if (entry.row.isRemoved() || entry.isBubbleDismissed() || entry.row.isDismissed()) {
+ if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
viewsToRemove.add(bv);
}
}
@@ -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/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index a79e047..6c47aac 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -120,7 +120,7 @@
* @return the view to display when the bubble is expanded.
*/
public ExpandableNotificationRow getRowView() {
- return mEntry.row;
+ return mEntry.getRow();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 21b21d9..eda3c59 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -177,9 +177,22 @@
log("state " + state);
}
- public static void traceWakeLockScreenWakeUp() {
+ /**
+ * Appends lock screen wake up event to the logs.
+ * @param wake if we're waking up or sleeping.
+ */
+ public static void traceLockScreenWakeUp(boolean wake) {
if (!ENABLED) return;
- log("wakeLockScreenWakeUp");
+ log("wakeLockScreenWakeUp " + wake);
+ }
+
+ /**
+ * Appends wake-display event to the logs.
+ * @param wake if we're waking up or sleeping.
+ */
+ public static void traceWakeDisplay(boolean wake) {
+ if (!ENABLED) return;
+ log("wakeLockScreenWakeUp " + wake);
}
public static void traceProximityResult(Context context, boolean near, long millis,
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 77f7ad4f..bf8e04d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -16,6 +16,7 @@
package com.android.systemui.doze;
+import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
import android.annotation.AnyThread;
@@ -67,7 +68,6 @@
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
private final Consumer<Boolean> mProxCallback;
- private final Consumer<Boolean> mWakeScreenCallback;
private final Callback mCallback;
private final Handler mHandler = new Handler();
@@ -76,8 +76,7 @@
public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
- Callback callback, Consumer<Boolean> proxCallback,
- Consumer<Boolean> wakeScreenCallback, AlwaysOnDisplayPolicy policy) {
+ Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
mContext = context;
mAlarmManager = alarmManager;
mSensorManager = sensorManager;
@@ -85,7 +84,6 @@
mConfig = config;
mWakeLock = wakeLock;
mProxCallback = proxCallback;
- mWakeScreenCallback = wakeScreenCallback;
mResolver = mContext.getContentResolver();
mSensors = new TriggerSensor[] {
@@ -123,7 +121,13 @@
DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
false /* reports touch coordinates */,
false /* touchscreen */),
- new WakeScreenSensor(),
+ new PluginTriggerSensor(
+ new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
+ Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
+ true /* configured */,
+ DozeLog.REASON_SENSOR_WAKE_UP,
+ false /* reports touch coordinates */,
+ false /* touchscreen */),
};
mProxSensor = new ProxSensor(policy);
@@ -395,7 +399,8 @@
screenX = event.values[0];
screenY = event.values[1];
}
- mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY);
+ mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY,
+ event.values);
updateListener(); // reregister, this sensor only fires once
}));
}
@@ -429,7 +434,14 @@
private final SensorManagerPlugin.Sensor mPluginSensor;
private final SensorManagerPlugin.TriggerEventListener mTriggerEventListener = (event) -> {
- onTrigger(null);
+ DozeLog.traceSensor(mContext, mPulseReason);
+ mHandler.post(mWakeLock.wrap(() -> {
+ if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
+ mRegistered = false;
+ mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
+ event.getValues());
+ updateListener(); // reregister, this sensor only fires once
+ }));
};
PluginTriggerSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
@@ -463,28 +475,19 @@
.append(", mSensor=").append(mPluginSensor).append("}").toString();
}
- }
-
- private class WakeScreenSensor extends TriggerSensor {
-
- WakeScreenSensor() {
- super(findSensorWithType(mConfig.wakeScreenSensorType()),
- Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, true /* configured */,
- DozeLog.REASON_SENSOR_WAKE_UP, false /* reportsTouchCoordinates */,
- false /* requiresTouchscreen */);
+ private String triggerEventToString(SensorManagerPlugin.TriggerEvent event) {
+ if (event == null) return null;
+ final StringBuilder sb = new StringBuilder("PluginTriggerEvent[")
+ .append(event.getSensor()).append(',')
+ .append(event.getVendorType());
+ if (event.getValues() != null) {
+ for (int i = 0; i < event.getValues().length; i++) {
+ sb.append(',').append(event.getValues()[i]);
+ }
+ }
+ return sb.append(']').toString();
}
- @Override
- @AnyThread
- public void onTrigger(TriggerEvent event) {
- DozeLog.traceSensor(mContext, mPulseReason);
- mHandler.post(mWakeLock.wrap(() -> {
- if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
- mRegistered = false;
- mWakeScreenCallback.accept(event.values[0] > 0);
- updateListener(); // reregister, this sensor only fires once
- }));
- }
}
public interface Callback {
@@ -494,11 +497,11 @@
* @param pulseReason Requesting sensor, e.g. {@link DozeLog#PULSE_REASON_SENSOR_PICKUP}
* @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity.
* @param screenX the location on the screen where the sensor fired or -1
- * if the sensor doesn't support reporting screen locations.
+ * if the sensor doesn't support reporting screen locations.
* @param screenY the location on the screen where the sensor fired or -1
- * if the sensor doesn't support reporting screen locations.
+ * @param rawValues raw values array from the event.
*/
void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck,
- float screenX, float screenY);
+ float screenX, float screenY, float[] rawValues);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index d69b1bf..bad0148 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -84,7 +84,7 @@
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar, this::onWakeScreen,
+ config, wakeLock, this::onSensor, this::onProximityFar,
dozeParameters.getPolicy());
mUiModeManager = mContext.getSystemService(UiModeManager.class);
}
@@ -124,13 +124,17 @@
}
private void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
- float screenX, float screenY) {
+ float screenX, float screenY, float[] rawValues) {
boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+ boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
+ boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
- if (isLongPress) {
+ if (isWakeDisplay) {
+ onWakeScreen(wakeEvent);
+ } else if (isLongPress) {
requestPulse(pulseReason, sensorPerformedProxCheck);
} else {
proximityCheckThenCall((result) -> {
@@ -141,7 +145,15 @@
if (isDoubleTap) {
mDozeHost.onDoubleTap(screenX, screenY);
mMachine.wakeUp();
- } else if (isPickup || isWakeLockScreen) {
+ } else if (isWakeLockScreen) {
+ if (wakeEvent) {
+ mDozeHost.setPassiveInterrupt(true);
+ mMachine.wakeUp();
+ DozeLog.traceLockScreenWakeUp(wakeEvent);
+ } else {
+ if (DEBUG) Log.d(TAG, "Unpulsing");
+ }
+ } else if (isPickup) {
mDozeHost.setPassiveInterrupt(true);
mMachine.wakeUp();
} else {
@@ -157,8 +169,6 @@
final boolean withinVibrationThreshold =
timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
- } else if (isWakeLockScreen) {
- DozeLog.traceWakeLockScreenWakeUp();
}
}
@@ -184,6 +194,7 @@
}
private void onWakeScreen(boolean wake) {
+ DozeLog.traceWakeDisplay(wake);
DozeMachine.State state = mMachine.getState();
boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
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/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index e78951a..201c7e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -18,7 +18,6 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -175,7 +174,7 @@
* @param builder The slice builder.
*/
protected void addZenMode(ListBuilder builder) {
- if (!isDndSuppressingNotifications()) {
+ if (!isDndOn()) {
return;
}
RowBuilder dndBuilder = new RowBuilder(mDndUri)
@@ -187,13 +186,10 @@
}
/**
- * Return true if DND is enabled suppressing notifications.
+ * Return true if DND is enabled.
*/
- protected boolean isDndSuppressingNotifications() {
- boolean suppressingNotifications = (mZenModeController.getConfig().suppressedVisualEffects
- & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
- return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF
- && suppressingNotifications;
+ protected boolean isDndOn() {
+ return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF;
}
@Override
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/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 35ae899..f054345 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -90,7 +90,7 @@
private float[] mRecentTemps = new float[MAX_RECENT_TEMPS];
private int mNumTemps;
private long mNextLogTime;
- private IThermalService mThermalService;
+ @VisibleForTesting IThermalService mThermalService;
@VisibleForTesting int mBatteryLevel = 100;
@VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
@@ -394,7 +394,7 @@
// Enable push notifications of throttling from vendor thermal
// management subsystem via thermalservice, in addition to our
// usual polling, to react to temperature jumps more quickly.
- IBinder b = ServiceManager.getService("thermalservice");
+ IBinder b = ServiceManager.getService(Context.THERMAL_SERVICE);
if (b != null) {
mThermalService = IThermalService.Stub.asInterface(b);
@@ -411,13 +411,8 @@
setNextLogTime();
- // This initialization method may be called on a configuration change. Only one set of
- // ongoing callbacks should be occurring, so remove any now. updateTemperatureWarning will
- // schedule an ongoing callback.
- mHandler.removeCallbacks(mUpdateTempCallback);
-
// We have passed all of the checks, start checking the temp
- updateTemperatureWarning();
+ mHandler.post(mUpdateTempCallback);
}
private void showThermalShutdownDialog() {
@@ -448,6 +443,8 @@
logTemperatureStats();
+ // Remove any pending callbacks as we only want to enable one
+ mHandler.removeCallbacks(mUpdateTempCallback);
mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
}
@@ -553,11 +550,7 @@
// Thermal event received from vendor thermal management subsystem
private final class ThermalEventListener extends IThermalEventListener.Stub {
@Override public void notifyThrottling(Temperature temp) {
- // Trigger an update of the temperature warning. Only one
- // callback can be enabled at a time, so remove any existing
- // callback; updateTemperatureWarning will schedule another one.
- mHandler.removeCallbacks(mUpdateTempCallback);
- updateTemperatureWarning();
+ mHandler.post(mUpdateTempCallback);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index d3715d0..65ed889 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -78,8 +78,9 @@
if (builder.app != null) {
text.setText(builder.app?.applicationName)
} else {
- text.text = context.getString(R.string.ongoing_privacy_chip_multiple_apps,
- builder.appsAndTypes.size)
+ text.text = context.resources.getQuantityString(
+ R.plurals.ongoing_privacy_chip_multiple_apps,
+ builder.appsAndTypes.size, builder.appsAndTypes.size)
}
}
} else {
@@ -100,9 +101,9 @@
context.getString(R.string.ongoing_privacy_chip_content_single_app,
builder.app?.applicationName, typesText)
} else {
- contentDescription = context.getString(
- R.string.ongoing_privacy_chip_content_multiple_apps_single_op,
- builder.appsAndTypes.size, typesText)
+ contentDescription = context.resources.getQuantityString(
+ R.plurals.ongoing_privacy_chip_content_multiple_apps_single_op,
+ builder.appsAndTypes.size, builder.appsAndTypes.size, typesText)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index f6a95af..bbdae29 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -20,6 +20,7 @@
import android.content.DialogInterface
import android.content.Intent
import android.content.res.ColorStateList
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
@@ -34,13 +35,13 @@
val dialogBuilder: PrivacyDialogBuilder
) {
- val iconSize = context.resources.getDimensionPixelSize(
- R.dimen.ongoing_appops_dialog_icon_height)
- val iconColor = context.resources.getColor(
+ private val iconSize = context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_dialog_icon_size)
+ private val iconColor = context.resources.getColor(
com.android.internal.R.color.text_color_primary, context.theme)
- companion object {
- private const val MAX_ITEMS = 10
- }
+ private val iconMargin = context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_dialog_icon_margin)
+ private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps)
fun createDialog(): Dialog {
val builder = AlertDialog.Builder(context).apply {
@@ -105,6 +106,11 @@
val appName = item.findViewById(R.id.app_name) as TextView
val icons = item.findViewById(R.id.icons) as LinearLayout
+ var lp = LinearLayout.LayoutParams(iconSize, iconSize).apply {
+ gravity = Gravity.CENTER_VERTICAL
+ marginStart = iconMargin
+ }
+
app.icon?.let {
appIcon.setImageDrawable(it)
}
@@ -117,7 +123,7 @@
imageTintList = ColorStateList.valueOf(iconColor)
setImageDrawable(it)
}
- icons.addView(image, iconSize, LinearLayout.LayoutParams.WRAP_CONTENT)
+ icons.addView(image, lp)
}
icons.visibility = View.VISIBLE
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 3fa3e8e..268462e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -18,10 +18,14 @@
import android.app.ActivityManager
import android.app.AppOpsManager
+import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.os.Handler
import android.os.UserHandle
import android.os.UserManager
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dependency
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
@@ -33,25 +37,29 @@
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION)
+ val intents = listOf(Intent.ACTION_USER_FOREGROUND,
+ Intent.ACTION_MANAGED_PROFILE_ADDED,
+ Intent.ACTION_MANAGED_PROFILE_REMOVED)
+ const val TAG = "PrivacyItemController"
}
private var privacyList = emptyList<PrivacyItem>()
private val appOpsController = Dependency.get(AppOpsController::class.java)
private val userManager = context.getSystemService(UserManager::class.java)
- private val currentUser = ActivityManager.getCurrentUser()
- private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+ private var currentUserIds = emptyList<Int>()
private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
+ private var listening = false
+
private val notifyChanges = Runnable {
callback.privacyChanged(privacyList)
}
+
private val updateListAndNotifyChanges = Runnable {
updatePrivacyList()
uiHandler.post(notifyChanges)
}
- private var listening = false
-
private val cb = object : AppOpsController.Callback {
override fun onActiveStateChanged(
code: Int,
@@ -61,12 +69,36 @@
) {
val userId = UserHandle.getUserId(uid)
if (userId in currentUserIds) {
- update()
+ update(false)
}
}
}
- private fun update() {
+ @VisibleForTesting
+ internal var userSwitcherReceiver = Receiver()
+ set(value) {
+ context.unregisterReceiver(field)
+ field = value
+ registerReceiver()
+ }
+
+ init {
+ registerReceiver()
+ }
+
+ private fun registerReceiver() {
+ context.registerReceiverAsUser(userSwitcherReceiver, UserHandle.ALL, IntentFilter().apply {
+ intents.forEach {
+ addAction(it)
+ }
+ }, null, null)
+ }
+
+ private fun update(updateUsers: Boolean) {
+ if (updateUsers) {
+ val currentUser = ActivityManager.getCurrentUser()
+ currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+ }
bgHandler.post(updateListAndNotifyChanges)
}
@@ -75,7 +107,7 @@
listening = listen
if (listening) {
appOpsController.addCallback(OPS, cb)
- update()
+ update(true)
} else {
appOpsController.removeCallback(OPS, cb)
}
@@ -102,4 +134,12 @@
interface Callback {
fun privacyChanged(privacyItems: List<PrivacyItem>)
}
+
+ internal inner class Receiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action in intents) {
+ update(true)
+ }
+ }
+ }
}
\ No newline at end of file
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/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/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/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 3da6d2e..bc38169 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -112,8 +112,7 @@
return;
}
- alertEntry.mEntry.row.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ alertEntry.mEntry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
if (alert) {
alertEntry.updateEntry(true /* updatePostTime */);
}
@@ -186,7 +185,7 @@
alertEntry.setEntry(entry);
mAlertEntries.put(entry.key, alertEntry);
onAlertEntryAdded(alertEntry);
- entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
/**
@@ -207,7 +206,7 @@
Entry entry = alertEntry.mEntry;
mAlertEntries.remove(key);
onAlertEntryRemoved(alertEntry);
- entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
alertEntry.reset();
if (mExtendedLifetimeAlertEntries.contains(entry)) {
if (mNotificationLifetimeFinishedCallback != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index f1c0304..a5e7f04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -79,7 +79,7 @@
@Override
protected void onAlertEntryAdded(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setAmbientPulsing(true);
+ entry.setAmbientPulsing(true);
for (OnAmbientChangedListener listener : mListeners) {
listener.onAmbientStateChanged(entry, true);
}
@@ -88,11 +88,11 @@
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setAmbientPulsing(false);
+ entry.setAmbientPulsing(false);
for (OnAmbientChangedListener listener : mListeners) {
listener.onAmbientStateChanged(entry, false);
}
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
}
@Override
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/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fc1e94a..f045548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -199,7 +199,7 @@
for (int i = 0; i < N; i++) {
final NotificationData.Entry entry = activeNotifications.get(i);
- if (isMediaNotification(entry)) {
+ if (entry.isMediaNotification()) {
final MediaSession.Token token =
entry.notification.getNotification().extras.getParcelable(
Notification.EXTRA_MEDIA_SESSION);
@@ -336,13 +336,6 @@
return PlaybackState.STATE_NONE;
}
- private boolean isMediaNotification(NotificationData.Entry entry) {
- // TODO: confirm that there's a valid media key
- return entry.row.getExpandedContentView() != null
- && entry.row.getExpandedContentView().findViewById(
- com.android.internal.R.id.media_actions) != null;
- }
-
private void clearCurrentMediaNotificationSession() {
mMediaMetadata = null;
if (mMediaController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 8c53cc2..7be5461 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -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();
}
@@ -393,7 +401,7 @@
}
public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
- if (entry.row == null || entry.row.isDismissed()) {
+ if (entry.isDismissed()) {
return false;
}
if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -403,7 +411,7 @@
}
public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
- if (entry.row == null || entry.row.isDismissed()) {
+ if (entry.isDismissed()) {
return false;
}
if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -532,7 +540,7 @@
// Ensure the entry hasn't already been removed. This can happen if there is an
// inflation exception while updating the remote history
- if (entry.row == null || entry.row.isRemoved()) {
+ if (entry.isRemoved()) {
return;
}
@@ -570,7 +578,7 @@
mEntryManager.updateNotification(newSbn, null);
- if (entry.row == null || entry.row.isRemoved()) {
+ if (entry.isRemoved()) {
return;
}
@@ -593,7 +601,7 @@
protected class RemoteInputActiveExtender extends RemoteInputExtender {
@Override
public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
- if (entry.row == null || entry.row.isDismissed()) {
+ if (entry.isDismissed()) {
return false;
}
return mRemoteInputController.isRemoteInputActive(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 37cc299..e7b4904 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();
}
@@ -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()
+ viewState.hasItemsInStableShelf = lastViewState.inShelf;
+ viewState.hidden = !mAmbientState.isShadeExpanded()
|| mAmbientState.isQsCustomizerShowing();
- mShelfState.maxShelfEnd = maxShelfEnd;
+ 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 ea67736..daa2fd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -51,6 +51,7 @@
public class NotificationViewHierarchyManager {
private static final String TAG = "NotificationViewHierarchyManager";
+ //TODO: change this top <Entry, List<Entry>>?
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
mTmpChildOrderMap = new HashMap<>();
@@ -140,6 +141,7 @@
/**
* Updates the visual representation of the notifications.
*/
+ //TODO: Rewrite this to focus on Entries, or some other data object instead of views
public void updateNotificationViews() {
ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
.getActiveNotifications();
@@ -148,12 +150,12 @@
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationData.Entry ent = activeNotifications.get(i);
- if (ent.row.isDismissed() || ent.row.isRemoved()) {
+ if (ent.isRowDismissed() || ent.isRowRemoved()) {
// we don't want to update removed notifications because they could
// temporarily become children if they were isolated before.
continue;
}
- ent.row.setStatusBarState(mStatusBarStateListener.getCurrentState());
+ ent.getRow().setStatusBarState(mStatusBarStateListener.getCurrentState());
boolean showAsBubble = ent.isBubble() && !ent.isBubbleDismissed()
&& mStatusBarStateListener.getCurrentState() == SHADE;
if (showAsBubble) {
@@ -175,20 +177,19 @@
boolean deviceSensitive = devicePublic
&& !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
mLockscreenUserManager.getCurrentUserId());
- ent.row.setSensitive(sensitive, deviceSensitive);
- ent.row.setNeedsRedaction(needsRedaction);
- if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
- ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
- ent.row.getStatusBarNotification());
+ ent.getRow().setSensitive(sensitive, deviceSensitive);
+ ent.getRow().setNeedsRedaction(needsRedaction);
+ if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
+ NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification);
List<ExpandableNotificationRow> orderedChildren =
- mTmpChildOrderMap.get(summary);
+ mTmpChildOrderMap.get(summary.getRow());
if (orderedChildren == null) {
orderedChildren = new ArrayList<>();
- mTmpChildOrderMap.put(summary, orderedChildren);
+ mTmpChildOrderMap.put(summary.getRow(), orderedChildren);
}
- orderedChildren.add(ent.row);
+ orderedChildren.add(ent.getRow());
} else {
- toShow.add(ent.row);
+ toShow.add(ent.getRow());
}
}
@@ -391,19 +392,19 @@
&& !row.isLowPriority()));
}
- entry.row.setOnAmbient(getShadeController().isDozing());
+ entry.getRow().setOnAmbient(getShadeController().isDozing());
int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
- entry.notification) && !entry.row.isRemoved();
+ entry.notification) && !entry.isRowRemoved();
boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
.notification);
if (!showOnKeyguard) {
// min priority notifications should show if their summary is showing
if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
- ExpandableNotificationRow summary = mGroupManager.getLogicalGroupSummary(
+ NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary(
entry.notification);
if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
- summary.getStatusBarNotification())) {
+ summary.notification)) {
showOnKeyguard = true;
}
}
@@ -411,16 +412,16 @@
if (suppressedSummary
|| mLockscreenUserManager.shouldHideNotifications(userId)
|| (onKeyguard && !showOnKeyguard)) {
- entry.row.setVisibility(View.GONE);
+ entry.getRow().setVisibility(View.GONE);
} else {
- boolean wasGone = entry.row.getVisibility() == View.GONE;
+ boolean wasGone = entry.getRow().getVisibility() == View.GONE;
if (wasGone) {
- entry.row.setVisibility(View.VISIBLE);
+ entry.getRow().setVisibility(View.VISIBLE);
}
- if (!isChildNotification && !entry.row.isRemoved()) {
+ if (!isChildNotification && !entry.getRow().isRemoved()) {
if (wasGone) {
// notify the scroller of a child addition
- mListContainer.generateAddAnimation(entry.row,
+ mListContainer.generateAddAnimation(entry.getRow(),
!showOnKeyguard /* fromMoreCard */);
}
visibleNotifications++;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 929f43e..e8abcc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -250,16 +250,16 @@
// Make a copy because closing the remote inputs will modify mOpen.
ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size());
for (int i = mOpen.size() - 1; i >= 0; i--) {
- NotificationData.Entry item = mOpen.get(i).first.get();
- if (item != null && item.row != null) {
- list.add(item);
+ NotificationData.Entry entry = mOpen.get(i).first.get();
+ if (entry != null && entry.rowExists()) {
+ list.add(entry);
}
}
for (int i = list.size() - 1; i >= 0; i--) {
- NotificationData.Entry item = list.get(i);
- if (item.row != null) {
- item.row.closeRemoteInput();
+ NotificationData.Entry entry = list.get(i);
+ if (entry.rowExists()) {
+ entry.closeRemoteInput();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 758c33a..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);
@@ -41,12 +47,34 @@
mCallback = callback;
}
- public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply) {
+ /**
+ * Notifies StatusBarService a smart reply is sent.
+ */
+ public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply,
+ boolean generatedByAssistant) {
mCallback.onSmartReplySent(entry, reply);
mSendingKeys.add(entry.key);
try {
- mBarService.onNotificationSmartReplySent(entry.notification.getKey(),
- replyIndex);
+ mBarService.onNotificationSmartReplySent(
+ entry.notification.getKey(), replyIndex, reply, generatedByAssistant);
+ } catch (RemoteException e) {
+ // Nothing to do, system going down
+ }
+ }
+
+ /**
+ * 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
}
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 da6d977..d7680b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -64,6 +64,9 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarIconView;
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;
@@ -106,7 +109,6 @@
public int importance;
public StatusBarIconView icon;
public StatusBarIconView expandedIcon;
- public ExpandableNotificationRow row; // the outer expanded view
private boolean interruption;
public boolean autoRedacted; // whether the redacted notification was generated by us
public int targetSdk;
@@ -119,6 +121,10 @@
public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
public CharSequence[] smartReplies = new CharSequence[0];
+ private Entry parent; // our parent (if we're in a group)
+ private ArrayList<Entry> children = new ArrayList<Entry>();
+ private ExpandableNotificationRow row; // the outer expanded view
+
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
private InflationTask mRunningTask = null;
@@ -212,6 +218,24 @@
}
}
+ public ExpandableNotificationRow getRow() {
+ return row;
+ }
+
+ //TODO: This will go away when we have a way to bind an entry to a row
+ public void setRow(ExpandableNotificationRow row) {
+ this.row = row;
+ }
+
+ @Nullable
+ public List<Entry> getChildren() {
+ if (children.size() <= 0) {
+ return null;
+ }
+
+ return children;
+ }
+
public void notifyFullScreenIntentLaunched() {
setInterruption();
lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
@@ -409,6 +433,198 @@
initializationTime = time;
}
}
+
+ public void sendAccessibilityEvent(int eventType) {
+ if (row != null) {
+ row.sendAccessibilityEvent(eventType);
+ }
+ }
+
+ /**
+ * Used by NotificationMediaManager to determine... things
+ * @return {@code true} if we are a media notification
+ */
+ public boolean isMediaNotification() {
+ if (row == null) return false;
+
+ return row.isMediaRow();
+ }
+
+ /**
+ * We are a top level child if our parent is the list of notifications duh
+ * @return {@code true} if we're a top level notification
+ */
+ public boolean isTopLevelChild() {
+ return row != null && row.isTopLevelChild();
+ }
+
+ public void resetUserExpansion() {
+ if (row != null) row.resetUserExpansion();
+ }
+
+ public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
+ if (row != null) row.freeContentViewWhenSafe(inflationFlag);
+ }
+
+ public void setAmbientPulsing(boolean pulsing) {
+ if (row != null) row.setAmbientPulsing(pulsing);
+ }
+
+ public boolean rowExists() {
+ return row != null;
+ }
+
+ public boolean isRowDismissed() {
+ return row != null && row.isDismissed();
+ }
+
+ public boolean isRowRemoved() {
+ return row != null && row.isRemoved();
+ }
+
+ /**
+ * @return {@code true} if the row is null or removed
+ */
+ public boolean isRemoved() {
+ //TODO: recycling invalidates this
+ return row == null || row.isRemoved();
+ }
+
+ /**
+ * @return {@code true} if the row is null or dismissed
+ */
+ public boolean isDismissed() {
+ //TODO: recycling
+ return row == null || row.isDismissed();
+ }
+
+ public boolean isRowPinned() {
+ return row != null && row.isPinned();
+ }
+
+ public void setRowPinned(boolean pinned) {
+ if (row != null) row.setPinned(pinned);
+ }
+
+ public boolean isRowAnimatingAway() {
+ return row != null && row.isHeadsUpAnimatingAway();
+ }
+
+ public boolean isRowHeadsUp() {
+ return row != null && row.isHeadsUp();
+ }
+
+ public void setHeadsUp(boolean shouldHeadsUp) {
+ if (row != null) row.setHeadsUp(shouldHeadsUp);
+ }
+
+ public boolean mustStayOnScreen() {
+ return row != null && row.mustStayOnScreen();
+ }
+
+ public void setHeadsUpIsVisible() {
+ if (row != null) row.setHeadsUpIsVisible();
+ }
+
+ //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
+ public ExpandableNotificationRow getHeadsUpAnimationView() {
+ return row;
+ }
+
+ public void setUserLocked(boolean userLocked) {
+ if (row != null) row.setUserLocked(userLocked);
+ }
+
+ public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
+ if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
+ }
+
+ public void setGroupExpansionChanging(boolean changing) {
+ if (row != null) row.setGroupExpansionChanging(changing);
+ }
+
+ public void notifyHeightChanged(boolean needsAnimation) {
+ if (row != null) row.notifyHeightChanged(needsAnimation);
+ }
+
+ public void closeRemoteInput() {
+ if (row != null) row.closeRemoteInput();
+ }
+
+ public boolean areChildrenExpanded() {
+ return row != null && row.areChildrenExpanded();
+ }
+
+ public boolean keepInParent() {
+ return row != null && row.keepInParent();
+ }
+
+ //TODO: probably less confusing to say "is group fully visible"
+ public boolean isGroupNotFullyVisible() {
+ return row == null || row.isGroupNotFullyVisible();
+ }
+
+ public NotificationGuts getGuts() {
+ if (row != null) return row.getGuts();
+ return null;
+ }
+
+ public boolean hasLowPriorityStateUpdated() {
+ return row != null && row.hasLowPriorityStateUpdated();
+ }
+
+ public void removeRow() {
+ if (row != null) row.setRemoved();
+ }
+
+ public boolean isSummaryWithChildren() {
+ return row != null && row.isSummaryWithChildren();
+ }
+
+ public void setKeepInParent(boolean keep) {
+ if (row != null) row.setKeepInParent(keep);
+ }
+
+ public void onDensityOrFontScaleChanged() {
+ if (row != null) row.onDensityOrFontScaleChanged();
+ }
+
+ public boolean areGutsExposed() {
+ return row != null && row.getGuts().isExposed();
+ }
+
+ public boolean isChildInGroup() {
+ return parent == null;
+ }
+
+ public void setLowPriorityStateUpdated(boolean updated) {
+ if (row != null) row.setLowPriorityStateUpdated(updated);
+ }
+
+ /**
+ * @return Can the underlying notification be cleared? This can be different from whether the
+ * notification can be dismissed in case notifications are sensitive on the lockscreen.
+ * @see #canViewBeDismissed()
+ */
+ public boolean isClearable() {
+ if (notification == null || !notification.isClearable()) {
+ return false;
+ }
+ if (children.size() > 0) {
+ for (int i = 0; i < children.size(); i++) {
+ Entry child = children.get(i);
+ if (!child.isClearable()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public boolean canViewBeDismissed() {
+ if (row == null) return true;
+ return row.canViewBeDismissed();
+ }
}
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
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 0818513..7970f16 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;
@@ -108,13 +107,13 @@
VisualStabilityManager.Callback, BubbleController.BubbleDismissListener {
private static final String TAG = "NotificationEntryMgr";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- protected static final boolean ENABLE_HEADS_UP = true;
- protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+ private static final boolean ENABLE_HEADS_UP = true;
+ private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
- protected final NotificationMessagingUtil mMessagingUtil;
+ private final NotificationMessagingUtil mMessagingUtil;
protected final Context mContext;
protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
- protected final NotificationClicker mNotificationClicker = new NotificationClicker();
+ private final NotificationClicker mNotificationClicker = new NotificationClicker();
private final NotificationGroupManager mGroupManager =
Dependency.get(NotificationGroupManager.class);
@@ -145,14 +144,15 @@
private NotificationPresenter mPresenter;
private Callback mCallback;
protected PowerManager mPowerManager;
- protected NotificationListenerService.RankingMap mLatestRankingMap;
+ private NotificationListenerService.RankingMap mLatestRankingMap;
protected HeadsUpManager mHeadsUpManager;
protected NotificationData mNotificationData;
- protected ContentObserver mHeadsUpObserver;
+ private ContentObserver mHeadsUpObserver;
protected boolean mUseHeadsUp = false;
- protected boolean mDisableNotificationAlerts;
+ private boolean mDisableNotificationAlerts;
protected NotificationListContainer mListContainer;
- protected final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
+ @VisibleForTesting
+ final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
= new ArrayList<>();
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
@@ -384,9 +384,9 @@
entry.notification.getUser().getIdentifier());
final StatusBarNotification sbn = entry.notification;
- if (entry.row != null) {
+ if (entry.rowExists()) {
entry.reset();
- updateNotification(entry, pmUser, sbn, entry.row);
+ updateNotification(entry, pmUser, sbn, entry.getRow());
} else {
new RowInflaterTask().inflate(mContext, parent, entry,
row -> {
@@ -485,22 +485,6 @@
updateNotifications();
}
- /**
- * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
- * about the failure.
- *
- * WARNING: this will call back into us. Don't hold any locks.
- */
- void handleNotificationError(StatusBarNotification n, String message) {
- removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
- try {
- mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
- n.getInitialPid(), message, n.getUserId());
- } catch (RemoteException ex) {
- // The end is nigh.
- }
- }
-
private void abortExistingInflation(String key) {
if (mPendingNotifications.containsKey(key)) {
NotificationData.Entry entry = mPendingNotifications.get(key);
@@ -513,13 +497,31 @@
}
}
+ /**
+ * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+ * about the failure.
+ *
+ * WARNING: this will call back into us. Don't hold any locks.
+ */
@Override
- public void handleInflationException(StatusBarNotification notification, Exception e) {
- handleNotificationError(notification, e.getMessage());
+ public void handleInflationException(StatusBarNotification n, Exception e) {
+ removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
+ try {
+ mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
+ n.getInitialPid(), e.getMessage(), n.getUserId());
+ } catch (RemoteException ex) {
+ // The end is nigh.
+ }
}
private void addEntry(NotificationData.Entry shadeEntry) {
- addNotificationViews(shadeEntry);
+ if (shadeEntry == null) {
+ return;
+ }
+ // Add the expanded view and icon.
+ mNotificationData.add(shadeEntry);
+ tagForeground(shadeEntry.notification);
+ updateNotifications();
mCallback.onNotificationAdded(shadeEntry);
}
@@ -540,14 +542,14 @@
// Mark as seen immediately
setNotificationShown(entry.notification);
} else {
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
}
}
if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
if (shouldPulse(entry)) {
mAmbientPulseManager.showNotification(entry);
} else {
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
}
}
}
@@ -558,20 +560,20 @@
mPendingNotifications.remove(entry.key);
// If there was an async task started after the removal, we don't want to add it back to
// the list, otherwise we might get leaks.
- if (!entry.row.isRemoved()) {
+ if (!entry.isRowRemoved()) {
boolean isNew = mNotificationData.get(entry.key) == null;
if (isNew) {
showAlertingView(entry, inflatedFlags);
addEntry(entry);
} else {
- if (entry.row.hasLowPriorityStateUpdated()) {
+ if (entry.getRow().hasLowPriorityStateUpdated()) {
mVisualStabilityManager.onLowPriorityUpdated(entry);
mPresenter.updateNotificationViews();
}
mGroupAlertTransferHelper.onInflationFinished(entry);
}
}
- entry.row.setLowPriorityStateUpdated(false);
+ entry.setLowPriorityStateUpdated(false);
}
@Override
@@ -630,9 +632,9 @@
getMediaManager().onNotificationRemoved(key);
mForegroundServiceController.removeNotification(entry.notification);
- if (entry.row != null) {
- entry.row.setRemoved();
- mListContainer.cleanUpViewState(entry.row);
+ if (entry.rowExists()) {
+ entry.removeRow();
+ mListContainer.cleanUpViewStateForEntry(entry);
}
// Let's remove the children if this was a summary
@@ -667,19 +669,19 @@
*/
private void handleGroupSummaryRemoved(String key) {
NotificationData.Entry entry = mNotificationData.get(key);
- if (entry != null && entry.row != null
- && entry.row.isSummaryWithChildren()) {
- if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
+ if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
+ if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
// We don't want to remove children for autobundled notifications as they are not
// always cancelled. We only remove them if they were dismissed by the user.
return;
}
- List<ExpandableNotificationRow> notificationChildren =
- entry.row.getNotificationChildren();
- for (int i = 0; i < notificationChildren.size(); i++) {
- ExpandableNotificationRow row = notificationChildren.get(i);
- NotificationData.Entry childEntry = row.getEntry();
- boolean isForeground = (row.getStatusBarNotification().getNotification().flags
+ List<NotificationData.Entry> childEntries = entry.getChildren();
+ if (childEntries == null) {
+ return;
+ }
+ for (int i = 0; i < childEntries.size(); i++) {
+ NotificationData.Entry childEntry = childEntries.get(i);
+ boolean isForeground = (entry.notification.getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) != 0;
boolean keepForReply =
getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
@@ -689,10 +691,10 @@
// a child we're keeping around for reply!
continue;
}
- row.setKeepInParent(true);
+ entry.setKeepInParent(true);
// we need to set this state earlier as otherwise we might generate some weird
// animations
- row.setRemoved();
+ entry.removeRow();
}
}
}
@@ -702,15 +704,15 @@
mNotificationData.getNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
NotificationData.Entry entry = userNotifications.get(i);
- boolean exposedGuts = mGutsManager.getExposedGuts() != null
- && entry.row.getGuts() == mGutsManager.getExposedGuts();
- entry.row.onDensityOrFontScaleChanged();
+ entry.onDensityOrFontScaleChanged();
+ boolean exposedGuts = entry.areGutsExposed();
if (exposedGuts) {
- mGutsManager.onDensityOrFontScaleChanged(entry.row);
+ mGutsManager.onDensityOrFontScaleChanged(entry);
}
}
}
+ //TODO: This method associates a row with an entry, but eventually needs to not do that
protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row) {
boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
@@ -733,8 +735,8 @@
entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
- entry.row = row;
- entry.row.setOnActivatedListener(mPresenter);
+ entry.setRow(row);
+ row.setOnActivatedListener(mPresenter);
boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
mNotificationData.getImportance(sbn.getKey()));
@@ -755,17 +757,7 @@
row.inflateViews();
}
- protected void addNotificationViews(NotificationData.Entry entry) {
- if (entry == null) {
- return;
- }
- // Add the expanded view and icon.
- mNotificationData.add(entry);
- tagForeground(entry.notification);
- updateNotifications();
- }
-
- protected NotificationData.Entry createNotificationViews(
+ private NotificationData.Entry createNotificationViews(
StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
throws InflationException {
if (DEBUG) {
@@ -773,7 +765,7 @@
}
NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
- if (shouldAutoBubble(entry)) {
+ if (BubbleController.shouldAutoBubble(getContext(), entry)) {
entry.setIsBubble(true);
}
@@ -841,7 +833,7 @@
}
@VisibleForTesting
- protected void tagForeground(StatusBarNotification notification) {
+ void tagForeground(StatusBarNotification notification) {
ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
notification.getUserId(), notification.getPackageName());
if (activeOps != null) {
@@ -917,7 +909,7 @@
if (!notification.isClearable()) {
// The user may have performed a dismiss action on the notification, since it's
// not clearable we should snap it back.
- mListContainer.snapViewIfNeeded(entry.row);
+ mListContainer.snapViewIfNeeded(entry);
}
if (DEBUG) {
@@ -970,11 +962,11 @@
if (NotificationUiAdjustment.needReinflate(
oldAdjustments.get(entry.key), newAdjustment)) {
- if (entry.row != null) {
+ if (entry.rowExists()) {
entry.reset();
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
entry.notification.getUser().getIdentifier());
- updateNotification(entry, pmUser, entry.notification, entry.row);
+ updateNotification(entry, pmUser, entry.notification, entry.getRow());
} else {
// Once the RowInflaterTask is done, it will pick up the updated entry, so
// no-op here.
@@ -1098,7 +1090,7 @@
* @param entry the entry to check
* @return true if the entry should ambient pulse, false otherwise
*/
- protected boolean shouldPulse(NotificationData.Entry entry) {
+ private boolean shouldPulse(NotificationData.Entry entry) {
StatusBarNotification sbn = entry.notification;
if (!getShadeController().isDozing()) {
@@ -1173,7 +1165,7 @@
return true;
}
- protected void setNotificationShown(StatusBarNotification n) {
+ private void setNotificationShown(StatusBarNotification n) {
setNotificationsShown(new String[]{n.getKey()});
}
@@ -1185,7 +1177,7 @@
}
}
- protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+ private boolean isSnoozedPackage(StatusBarNotification sbn) {
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
@@ -1214,17 +1206,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/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 53ebe74..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,18 +16,16 @@
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 row
+ * @param entry
* @return true if row is in a visible location
*/
- boolean isInVisibleLocation(ExpandableNotificationRow row);
+ boolean isInVisibleLocation(NotificationData.Entry entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 75613a4..fce7980 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -120,7 +120,7 @@
return true;
}
if (mAllowedReorderViews.contains(row)
- && !mVisibilityLocationProvider.isInVisibleLocation(row)) {
+ && !mVisibilityLocationProvider.isInVisibleLocation(row.getEntry())) {
return true;
}
return false;
@@ -142,12 +142,12 @@
if (isHeadsUp) {
// Heads up notifications should in general be allowed to reorder if they are out of
// view and stay at the current location if they aren't.
- mAllowedReorderViews.add(entry.row);
+ mAllowedReorderViews.add(entry.getRow());
}
}
public void onLowPriorityUpdated(NotificationData.Entry entry) {
- mLowPriorityReorderingViews.add(entry.row);
+ mLowPriorityReorderingViews.add(entry.getRow());
}
/**
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 5dfd5d0..87313b8 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
@@ -115,7 +115,7 @@
for (int i = 0; i < N; i++) {
NotificationData.Entry entry = activeNotifications.get(i);
String key = entry.notification.getKey();
- boolean isVisible = mListContainer.isInVisibleLocation(entry.row);
+ boolean isVisible = mListContainer.isInVisibleLocation(entry);
NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
if (isVisible) {
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 b6d99b2..0cd431f 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;
@@ -104,7 +96,7 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -336,7 +328,6 @@
private float mTranslationWhenRemoved;
private boolean mWasChildInGroupWhenRemoved;
private int mNotificationColorAmbient;
- private NotificationViewState mNotificationViewState;
private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
new SystemNotificationAsyncTask();
@@ -893,29 +884,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);
}
}
@@ -1411,16 +1405,16 @@
public void performDismiss(boolean fromAccessibility) {
if (isOnlyChildInGroup()) {
- ExpandableNotificationRow groupSummary =
+ NotificationData.Entry groupSummary =
mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
if (groupSummary.isClearable()) {
// If this is the only child in the group, dismiss the group, but don't try to show
// the blocking helper affordance!
- groupSummary.performDismiss(fromAccessibility);
+ groupSummary.getRow().performDismiss(fromAccessibility);
}
}
setDismissed(fromAccessibility);
- if (isClearable()) {
+ if (mEntry.isClearable()) {
// TODO: track dismiss sentiment
if (mOnDismissRunnable != null) {
mOnDismissRunnable.run();
@@ -2046,7 +2040,7 @@
.setInterpolator(Interpolators.ALPHA_OUT);
setAboveShelf(true);
mExpandAnimationRunning = true;
- mNotificationViewState.cancelAnimations(this);
+ getViewState().cancelAnimations(this);
mNotificationLaunchHeight = AmbientState.getNotificationLaunchHeight(getContext());
} else {
mExpandAnimationRunning = false;
@@ -2244,28 +2238,6 @@
setRippleAllowed(allowed);
}
- /**
- * @return Can the underlying notification be cleared? This can be different from whether the
- * notification can be dismissed in case notifications are sensitive on the lockscreen.
- * @see #canViewBeDismissed()
- */
- public boolean isClearable() {
- if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
- return false;
- }
- if (mIsSummaryWithChildren) {
- List<ExpandableNotificationRow> notificationChildren =
- mChildrenContainer.getNotificationChildren();
- for (int i = 0; i < notificationChildren.size(); i++) {
- ExpandableNotificationRow child = notificationChildren.get(i);
- if (!child.isClearable()) {
- return false;
- }
- }
- }
- return true;
- }
-
@Override
public int getIntrinsicHeight() {
if (isShownAsBubble()) {
@@ -2533,10 +2505,10 @@
/**
* @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
* otherwise some state might not be updated. To request about the general clearability
- * see {@link #isClearable()}.
+ * see {@link NotificationData.Entry#isClearable()}.
*/
public boolean canViewBeDismissed() {
- return isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
}
private boolean shouldShowPublic() {
@@ -2945,13 +2917,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
@@ -3038,6 +3005,21 @@
return mOnAmbient;
}
+ //TODO: this logic can't depend on layout if we are recycling!
+ public boolean isMediaRow() {
+ return getExpandedContentView() != null
+ && getExpandedContentView().findViewById(
+ com.android.internal.R.id.media_actions) != null;
+ }
+
+ public boolean isTopLevelChild() {
+ return getParent() instanceof NotificationStackScrollLayout;
+ }
+
+ public boolean isGroupNotFullyVisible() {
+ return getClipTopAmount() > 0 || getTranslationY() < 0;
+ }
+
public void setAboveShelf(boolean aboveShelf) {
boolean wasAboveShelf = isAboveShelf();
mAboveShelf = aboveShelf;
@@ -3046,14 +3028,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) {
@@ -3064,7 +3039,7 @@
}
handleFixedTranslationZ(row);
super.applyToView(view);
- row.applyChildrenState(mOverallState);
+ row.applyChildrenState();
}
}
@@ -3095,7 +3070,7 @@
}
handleFixedTranslationZ(row);
super.animateTo(child, properties);
- row.startChildAnimation(mOverallState, properties);
+ row.startChildAnimation(properties);
}
}
}
@@ -3145,11 +3120,13 @@
pw.print(", alpha: " + getAlpha());
pw.print(", translation: " + getTranslation());
pw.print(", removed: " + isRemoved());
- pw.print(", privateShowing: " + (getShowingLayout() == mPrivateLayout));
+ NotificationContentView showingLayout = getShowingLayout();
+ pw.print(", privateShowing: " + (showingLayout == mPrivateLayout));
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/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 fa3fa5b..92d1b45 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
@@ -55,6 +55,10 @@
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.SmartReplyView;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
/**
* A frame layout containing the actual payload of the notification, including the contracted,
* expanded and heads up layout. This class is responsible for clipping the content and and
@@ -1225,17 +1229,18 @@
mOnContentViewInactiveListeners.clear();
mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
updateAllSingleLineViews();
+ ExpandableNotificationRow row = entry.getRow();
if (mContractedChild != null) {
- mContractedWrapper.onContentUpdated(entry.row);
+ mContractedWrapper.onContentUpdated(row);
}
if (mExpandedChild != null) {
- mExpandedWrapper.onContentUpdated(entry.row);
+ mExpandedWrapper.onContentUpdated(row);
}
if (mHeadsUpChild != null) {
- mHeadsUpWrapper.onContentUpdated(entry.row);
+ mHeadsUpWrapper.onContentUpdated(row);
}
if (mAmbientChild != null) {
- mAmbientWrapper.onContentUpdated(entry.row);
+ mAmbientWrapper.onContentUpdated(row);
}
applyRemoteInputAndSmartReply(entry);
updateLegacy();
@@ -1285,38 +1290,77 @@
return;
}
- Notification notification = entry.notification.getNotification();
+ SmartRepliesAndActions smartRepliesAndActions =
+ chooseSmartRepliesAndActions(mSmartReplyConstants, entry);
- Pair<RemoteInput, Notification.Action> remoteInputActionPair =
- entry.notification.getNotification().findRemoteInputActionPair(false /*freeform */);
- Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
- notification.findRemoteInputActionPair(true /*freeform */);
+ applyRemoteInput(entry, smartRepliesAndActions.hasFreeformRemoteInput);
+ applySmartReplyView(smartRepliesAndActions, entry);
+ }
- boolean enableAppGeneratedSmartReplies = (mSmartReplyConstants.isEnabled()
- && (!mSmartReplyConstants.requiresTargetingP()
+ /**
+ * Chose what smart replies and smart actions to display. App generated suggestions take
+ * precedence. So if the app provides any smart replies, we don't show any
+ * replies or actions generated by the NotificationAssistantService (NAS), and if the app
+ * provides any smart actions we also don't show any NAS-generated replies or actions.
+ */
+ @VisibleForTesting
+ static SmartRepliesAndActions chooseSmartRepliesAndActions(
+ SmartReplyConstants smartReplyConstants,
+ final NotificationData.Entry entry) {
+ boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
+ && (!smartReplyConstants.requiresTargetingP()
|| entry.targetSdk >= Build.VERSION_CODES.P));
- RemoteInput remoteInputWithChoices = null;
- PendingIntent pendingIntentWithChoices= null;
- CharSequence[] choices = null;
- if (enableAppGeneratedSmartReplies
- && remoteInputActionPair != null
- && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())) {
- // app generated smart replies
- remoteInputWithChoices = remoteInputActionPair.first;
- pendingIntentWithChoices = remoteInputActionPair.second.actionIntent;
- choices = remoteInputActionPair.first.getChoices();
- } else if (!ArrayUtils.isEmpty(entry.smartReplies)
- && freeformRemoteInputActionPair != null
- && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()) {
- // system generated smart replies
- remoteInputWithChoices = freeformRemoteInputActionPair.first;
- pendingIntentWithChoices = freeformRemoteInputActionPair.second.actionIntent;
- choices = entry.smartReplies;
- }
+ Notification notification = entry.notification.getNotification();
+ Pair<RemoteInput, Notification.Action> remoteInputActionPair =
+ notification.findRemoteInputActionPair(false /* freeform */);
+ Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
+ notification.findRemoteInputActionPair(true /* freeform */);
- applyRemoteInput(entry, freeformRemoteInputActionPair != null);
- applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry, choices);
+ boolean appGeneratedSmartRepliesExist =
+ enableAppGeneratedSmartReplies
+ && remoteInputActionPair != null
+ && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())
+ && remoteInputActionPair.second.actionIntent != null;
+
+ List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
+ boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
+
+ SmartReplyView.SmartReplies smartReplies = null;
+ SmartReplyView.SmartActions smartActions = null;
+ if (appGeneratedSmartRepliesExist) {
+ smartReplies = new SmartReplyView.SmartReplies(
+ remoteInputActionPair.first.getChoices(),
+ remoteInputActionPair.first,
+ remoteInputActionPair.second.actionIntent,
+ false /* fromAssistant */);
+ }
+ if (appGeneratedSmartActionsExist) {
+ smartActions = new SmartReplyView.SmartActions(appGeneratedSmartActions,
+ false /* fromAssistant */);
+ }
+ // Apps didn't provide any smart replies / actions, use those from NAS (if any).
+ if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
+ boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.smartReplies)
+ && freeformRemoteInputActionPair != null
+ && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
+ && freeformRemoteInputActionPair.second.actionIntent != null;
+ if (useGeneratedReplies) {
+ smartReplies = new SmartReplyView.SmartReplies(
+ entry.smartReplies,
+ freeformRemoteInputActionPair.first,
+ freeformRemoteInputActionPair.second.actionIntent,
+ true /* fromAssistant */);
+ }
+ boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions)
+ && notification.getAllowSystemGeneratedContextualActions();
+ if (useSmartActions) {
+ smartActions = new SmartReplyView.SmartActions(
+ entry.systemGeneratedSmartActions, true /* fromAssistant */);
+ }
+ }
+ return new SmartRepliesAndActions(
+ smartReplies, smartActions, freeformRemoteInputActionPair != null);
}
private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
@@ -1418,28 +1462,29 @@
return null;
}
- private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
- NotificationData.Entry entry, CharSequence[] choices) {
+ private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions,
+ NotificationData.Entry entry) {
if (mExpandedChild != null) {
mExpandedSmartReplyView =
- applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry, choices);
- if (mExpandedSmartReplyView != null && remoteInput != null
- && choices != null && choices.length > 0) {
- mSmartReplyController.smartRepliesAdded(entry, choices.length);
+ applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
+ if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) {
+ mSmartReplyController.smartRepliesAdded(
+ entry, smartRepliesAndActions.smartReplies.choices.length);
}
}
}
- private SmartReplyView applySmartReplyView(
- View view, RemoteInput remoteInput, PendingIntent pendingIntent,
- NotificationData.Entry entry, CharSequence[] choices) {
+ private SmartReplyView applySmartReplyView(View view,
+ SmartRepliesAndActions smartRepliesAndActions, NotificationData.Entry entry) {
View smartReplyContainerCandidate = view.findViewById(
com.android.internal.R.id.smart_reply_container);
if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
return null;
}
LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
- if (remoteInput == null || pendingIntent == null) {
+ // If there are no smart replies and no smart actions - early out.
+ if (smartRepliesAndActions.smartReplies == null
+ && smartRepliesAndActions.smartActions == null) {
smartReplyContainer.setVisibility(View.GONE);
return null;
}
@@ -1468,9 +1513,15 @@
}
}
if (smartReplyView != null) {
- smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
- mSmartReplyController, entry, smartReplyContainer, choices
- );
+ smartReplyView.resetSmartSuggestions(smartReplyContainer);
+ if (smartRepliesAndActions.smartReplies != null) {
+ smartReplyView.addRepliesFromRemoteInput(
+ smartRepliesAndActions.smartReplies, mSmartReplyController, entry);
+ }
+ if (smartRepliesAndActions.smartActions != null) {
+ smartReplyView.addSmartActions(
+ smartRepliesAndActions.smartActions, mSmartReplyController, entry);
+ }
smartReplyContainer.setVisibility(View.VISIBLE);
}
return smartReplyView;
@@ -1870,4 +1921,41 @@
mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
}
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print(" ");
+ pw.print("contentView visibility: " + getVisibility());
+ pw.print(", alpha: " + getAlpha());
+ pw.print(", clipBounds: " + getClipBounds());
+ pw.print(", contentHeight: " + mContentHeight);
+ pw.print(", visibleType: " + mVisibleType);
+ View view = getViewForVisibleType(mVisibleType);
+ pw.print(", visibleView ");
+ if (view != null) {
+ pw.print(" visibility: " + view.getVisibility());
+ pw.print(", alpha: " + view.getAlpha());
+ pw.print(", clipBounds: " + view.getClipBounds());
+ } else {
+ pw.print("null");
+ }
+ pw.println();
+ }
+
+ @VisibleForTesting
+ static class SmartRepliesAndActions {
+ @Nullable
+ public final SmartReplyView.SmartReplies smartReplies;
+ @Nullable
+ public final SmartReplyView.SmartActions smartActions;
+ public final boolean hasFreeformRemoteInput;
+
+ SmartRepliesAndActions(
+ @Nullable SmartReplyView.SmartReplies smartReplies,
+ @Nullable SmartReplyView.SmartActions smartActions,
+ boolean hasFreeformRemoteInput) {
+ this.smartReplies = smartReplies;
+ this.smartActions = smartActions;
+ this.hasFreeformRemoteInput = hasFreeformRemoteInput;
+ }
+ }
}
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 37bf06e..3b407b5 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;
@@ -86,7 +82,6 @@
private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
// which notification is currently being longpress-examined by the user
- private final IStatusBarService mBarService;
private NotificationGuts mNotificationGutsExposed;
private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
@@ -103,8 +98,6 @@
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -116,9 +109,9 @@
mOnSettingsClickListener = onSettingsClick;
}
- public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
- setExposedGuts(row.getGuts());
- bindGuts(row);
+ public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
+ setExposedGuts(entry.getGuts());
+ bindGuts(entry.getRow());
}
/**
@@ -191,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) {
@@ -256,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();
@@ -306,8 +291,7 @@
isForBlockingHelper,
row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
row.getEntry().noisy,
- row.getEntry().importance,
- action);
+ row.getEntry().importance);
}
@@ -441,8 +425,8 @@
public boolean shouldExtendLifetime(NotificationData.Entry entry) {
return entry != null
&&(mNotificationGutsExposed != null
- && entry.row.getGuts() != null
- && mNotificationGutsExposed == entry.row.getGuts()
+ && entry.getGuts() != null
+ && mNotificationGutsExposed == entry.getGuts()
&& !mNotificationGutsExposed.isLeavebehind());
}
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 3a7091b..213ac70 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
@@ -92,6 +92,7 @@
private String mPackageName;
private String mAppName;
private int mAppUid;
+ private String mDelegatePkg;
private int mNumUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
@@ -186,14 +187,13 @@
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);
+ importance);
}
public void bindNotification(
@@ -211,8 +211,7 @@
boolean isForBlockingHelper,
boolean isUserSentimentNegative,
boolean isNoisy,
- int importance,
- @NotificationInfoAction int action)
+ int importance)
throws RemoteException {
mINotificationManager = iNotificationManager;
mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -235,6 +234,7 @@
(mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
mIsForBlockingHelper = isForBlockingHelper;
mAppUid = mSbn.getUid();
+ mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
mIsNoisy = isNoisy;
@@ -253,10 +253,6 @@
bindHeader();
bindPrompt();
bindButtons();
-
- if (action != ACTION_NONE) {
- swapContent(action, false /* don't animate */);
- }
}
private void bindHeader() throws RemoteException {
@@ -281,26 +277,8 @@
((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
((TextView) findViewById(R.id.pkgname)).setText(mAppName);
- // Set group information if this channel has an associated group.
- CharSequence groupName = null;
- if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
- final NotificationChannelGroup notificationChannelGroup =
- mINotificationManager.getNotificationChannelGroupForPackage(
- mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
- if (notificationChannelGroup != null) {
- groupName = notificationChannelGroup.getName();
- }
- }
- TextView groupNameView = findViewById(R.id.group_name);
- TextView groupDividerView = findViewById(R.id.pkg_group_divider);
- if (groupName != null) {
- groupNameView.setText(groupName);
- groupNameView.setVisibility(View.VISIBLE);
- groupDividerView.setVisibility(View.VISIBLE);
- } else {
- groupNameView.setVisibility(View.GONE);
- groupDividerView.setVisibility(View.GONE);
- }
+ // Delegate
+ bindDelegate();
// Settings button.
final View settingsButton = findViewById(R.id.info);
@@ -320,9 +298,10 @@
}
}
- private void bindPrompt() {
+ private void bindPrompt() throws RemoteException {
final TextView blockPrompt = findViewById(R.id.block_prompt);
bindName();
+ bindGroup();
if (mIsNonblockable) {
blockPrompt.setText(R.string.notification_unblockable_desc);
} else {
@@ -345,6 +324,60 @@
}
}
+ private void bindDelegate() {
+ TextView delegateView = findViewById(R.id.delegate_name);
+ TextView dividerView = findViewById(R.id.pkg_divider);
+
+ CharSequence delegatePkg = null;
+ if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
+ // this notification was posted by a delegate!
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfo(
+ mDelegatePkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (info != null) {
+ delegatePkg = String.valueOf(mPm.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException e) { }
+ }
+ if (delegatePkg != null) {
+ delegateView.setText(mContext.getResources().getString(
+ R.string.notification_delegate_header, delegatePkg));
+ delegateView.setVisibility(View.VISIBLE);
+ dividerView.setVisibility(View.VISIBLE);
+ } else {
+ delegateView.setVisibility(View.GONE);
+ dividerView.setVisibility(View.GONE);
+ }
+ }
+
+ private void bindGroup() throws RemoteException {
+ // Set group information if this channel has an associated group.
+ CharSequence groupName = null;
+ if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
+ final NotificationChannelGroup notificationChannelGroup =
+ mINotificationManager.getNotificationChannelGroupForPackage(
+ mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
+ if (notificationChannelGroup != null) {
+ groupName = notificationChannelGroup.getName();
+ }
+ }
+ TextView groupNameView = findViewById(R.id.group_name);
+ TextView groupDividerView = findViewById(R.id.pkg_group_divider);
+ if (groupName != null) {
+ groupNameView.setText(groupName);
+ groupNameView.setVisibility(View.VISIBLE);
+ groupDividerView.setVisibility(View.VISIBLE);
+ } else {
+ groupNameView.setVisibility(View.GONE);
+ groupDividerView.setVisibility(View.GONE);
+ }
+ }
+
@VisibleForTesting
void logBlockingHelperCounter(String counterTag) {
if (mIsForBlockingHelper) {
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 c16b28f..948d2a5 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,30 @@
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);
+ mRightMenuItems.add(mSnoozeItem);
}
populateMenuViews();
@@ -367,6 +357,9 @@
public void onSnapOpen() {
mMenuSnapped = true;
mMenuSnappedOnLeft = isMenuOnLeft();
+ if (mAlpha == 0f && mParent != null) {
+ fadeInMenu(mParent.getWidth());
+ }
if (mMenuListener != null) {
mMenuListener.onMenuShown(getParent());
}
@@ -631,13 +624,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) {
@@ -648,29 +652,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) {
@@ -786,23 +767,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 4d100a4..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.
@@ -118,9 +115,9 @@
/**
* Handle snapping a non-dismissable row back if the user tried to dismiss it.
*
- * @param row row to snap back
+ * @param entry the entry whose row needs to snap back
*/
- void snapViewIfNeeded(ExpandableNotificationRow row);
+ void snapViewIfNeeded(NotificationData.Entry entry);
/**
* Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
@@ -149,9 +146,9 @@
* Called when a notification is removed from the shade. This cleans up the state for a
* given view.
*
- * @param view view to clean up view state for
+ * @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast)
*/
- void cleanUpViewState(View view);
+ void cleanUpViewStateForEntry(NotificationData.Entry entry);
/**
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 c867a41..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,13 +16,12 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
- .NUM_SECTIONS;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
-import android.view.View;
-
+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;
@@ -37,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;
@@ -50,13 +49,13 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
- updateView(headsUp, false /* animate */);
+ public void onHeadsUpPinned(NotificationData.Entry headsUp) {
+ updateView(headsUp.getRow(), false /* animate */);
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
- updateView(headsUp, true /* animate */);
+ public void onHeadsUpUnPinned(NotificationData.Entry headsUp) {
+ updateView(headsUp.getRow(), true /* animate */);
}
public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
@@ -211,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 ecd0d98..626e688 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
@@ -208,16 +208,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 +271,7 @@
private boolean mDontReportNextOverScroll;
private boolean mDontClampNextScroll;
private boolean mNeedViewResizeAnimation;
- private View mExpandedGroupView;
+ private ExpandableView mExpandedGroupView;
private boolean mEverythingNeedsAnimation;
/**
@@ -382,7 +378,7 @@
private boolean mGroupExpandedForMeasure;
private boolean mScrollable;
private View mForcedScroll;
- private View mNeedingPulseAnimation;
+ private ExpandableView mNeedingPulseAnimation;
/**
* @see #setDarkAmount(float, float)
@@ -600,12 +596,12 @@
public void setRemoteInputActive(NotificationData.Entry entry,
boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
- entry.row.notifyHeightChanged(true /* needsAnimation */);
+ entry.notifyHeightChanged(true /* needsAnimation */);
updateFooter();
}
public void lockScrollTo(NotificationData.Entry entry) {
- NotificationStackScrollLayout.this.lockScrollTo(entry.row);
+ NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
}
public void requestDisallowLongPressAndDismiss() {
@@ -897,8 +893,10 @@
@Override
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
- public boolean isInVisibleLocation(ExpandableNotificationRow row) {
- ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
+ public boolean isInVisibleLocation(NotificationData.Entry entry) {
+ ExpandableNotificationRow row = entry.getRow();
+ ExpandableViewState childViewState = row.getViewState();
+
if (childViewState == null) {
return false;
}
@@ -944,7 +942,7 @@
? 0
: mScroller.getCurrVelocity());
mAmbientState.setScrollY(mOwnScrollY);
- mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
+ mStackScrollAlgorithm.resetViewStates(mAmbientState);
if (!isCurrentlyAnimating() && !mNeedsAnimation) {
applyCurrentState();
} else {
@@ -1213,12 +1211,12 @@
if (topEntry == null) {
return 0;
}
- ExpandableNotificationRow row = topEntry.row;
+ ExpandableNotificationRow row = topEntry.getRow();
if (row.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
+ final NotificationData.Entry groupSummary
= mGroupManager.getGroupSummary(row.getStatusBarNotification());
if (groupSummary != null) {
- row = groupSummary;
+ row = groupSummary.getRow();
}
}
return row.getPinnedHeadsUpHeight();
@@ -1390,11 +1388,12 @@
&& touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
if (slidingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
+ NotificationData.Entry entry = row.getEntry();
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
- && mHeadsUpManager.getTopEntry().row != row
+ && mHeadsUpManager.getTopEntry().getRow() != row
&& mGroupManager.getGroupSummary(
- mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
- != row) {
+ mHeadsUpManager.getTopEntry().notification)
+ != entry) {
continue;
}
return row.getViewAtPosition(touchY - childTop);
@@ -1524,7 +1523,8 @@
@Override
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public void snapViewIfNeeded(ExpandableNotificationRow child) {
+ public void snapViewIfNeeded(NotificationData.Entry entry) {
+ ExpandableNotificationRow child = entry.getRow();
boolean animate = mIsExpanded || isPinnedHeadsUp(child);
// If the child is showing the notification menu snap to that
float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
@@ -1934,12 +1934,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;
@@ -2508,35 +2508,33 @@
// 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);
}
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@Override
- public void cleanUpViewState(View child) {
+ public void cleanUpViewStateForEntry(NotificationData.Entry entry) {
+ View child = entry.getRow();
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);
@@ -2580,14 +2578,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)) {
@@ -2644,9 +2642,9 @@
private boolean isChildInInvisibleGroup(View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- ExpandableNotificationRow groupSummary =
+ NotificationData.Entry groupSummary =
mGroupManager.getGroupSummary(row.getStatusBarNotification());
- if (groupSummary != null && groupSummary != row) {
+ if (groupSummary != null && groupSummary.getRow() != row) {
return row.getVisibility() == View.INVISIBLE;
}
}
@@ -2761,7 +2759,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onViewAdded(View child) {
super.onViewAdded(child);
- onViewAddedInternal(child);
+ onViewAddedInternal((ExpandableView) child);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -2831,31 +2829,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);
}
@@ -2927,7 +2922,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);
@@ -2944,7 +2939,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) {
@@ -2983,8 +2978,7 @@
}
if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
setAnimationRunning(true);
- mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
- mGoToFullShadeDelay);
+ mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
mAnimationEvents.clear();
updateBackground();
updateViewShadows();
@@ -3031,7 +3025,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.
@@ -3097,7 +3091,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
@@ -3139,7 +3133,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));
}
@@ -3153,7 +3147,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,
@@ -3245,7 +3239,7 @@
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
- return new StackScrollAlgorithm(context);
+ return new StackScrollAlgorithm(context, this);
}
/**
@@ -3910,7 +3904,7 @@
mStatusBar.resetUserExpandedStates();
clearTemporaryViews();
clearUserLockedViews();
- ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
+ ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
if (draggedViews.size() > 0) {
draggedViews.clear();
updateContinuousShadowDrawing();
@@ -4194,7 +4188,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();
}
@@ -4662,6 +4661,11 @@
mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
}
+ public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) {
+ ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
+ generateHeadsUpAnimation(row, isHeadsUp);
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
@@ -4933,8 +4937,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 {
@@ -4951,7 +4954,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++) {
@@ -5487,7 +5490,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;
@@ -5495,21 +5498,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;
@@ -5692,7 +5695,7 @@
&& (parent.areGutsExposed()
|| mSwipeHelper.getExposedMenuView() == parent
|| (parent.getNotificationChildren().size() == 1
- && parent.isClearable()))) {
+ && parent.getEntry().isClearable()))) {
// In this case the group is expanded and showing the menu for the
// group, further interaction should apply to the group, not any
// child notifications so we use the parent of the child. We also do the same
@@ -5707,7 +5710,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..713bd90 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
@@ -128,25 +128,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 +159,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 +173,7 @@
|| viewState.clipTopAmount != child.getClipTopAmount()
|| viewState.dark != child.isDark())) {
mAnimationProperties.delay = mCurrentAdditionalDelay
- + calculateChildAnimationDelay(viewState, finalState);
+ + calculateChildAnimationDelay(viewState);
}
}
@@ -193,8 +193,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 +213,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 +229,7 @@
return -1;
}
- private long calculateChildAnimationDelay(ExpandableViewState viewState,
- StackScrollState finalState) {
+ private long calculateChildAnimationDelay(ExpandableViewState viewState) {
if (mAnimationFilter.hasGoToFullShadeEvent) {
return calculateDelayGoToFullShade(viewState);
}
@@ -244,8 +242,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 +256,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 +266,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 +348,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,8 +375,8 @@
// 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);
+ ExpandableViewState viewState =
+ ((ExpandableView) event.viewAfterChangingView).getViewState();
int actualHeight = changingView.getActualHeight();
// upwards by default
float translationDirection = -1.0f;
@@ -426,11 +420,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 +433,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 +448,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/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 40f9f45..3c8cad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -143,9 +143,9 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
updateTopEntry();
- updateHeader(headsUp.getEntry());
+ updateHeader(entry);
}
/** To count the distance from the window right boundary to scroller right boundary. The
@@ -298,9 +298,9 @@
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
updateTopEntry();
- updateHeader(headsUp.getEntry());
+ updateHeader(entry);
}
public void setExpandedHeight(float expandedHeight, float appearFraction) {
@@ -339,7 +339,7 @@
}
public void updateHeader(NotificationData.Entry entry) {
- ExpandableNotificationRow row = entry.row;
+ ExpandableNotificationRow row = entry.getRow();
float headerVisibleAmount = 1.0f;
if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
headerVisibleAmount = mExpandFraction;
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 00d6b14..9faada0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -304,18 +304,19 @@
return;
}
if (hasPinnedHeadsUp()) {
- ExpandableNotificationRow topEntry = getTopEntry().row;
+ NotificationData.Entry topEntry = getTopEntry();
if (topEntry.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+ final NotificationData.Entry groupSummary
+ = mGroupManager.getGroupSummary(topEntry.notification);
if (groupSummary != null) {
topEntry = groupSummary;
}
}
- topEntry.getLocationOnScreen(mTmpTwoArray);
+ ExpandableNotificationRow topRow = topEntry.getRow();
+ topRow.getLocationOnScreen(mTmpTwoArray);
int minX = mTmpTwoArray[0];
- int maxX = mTmpTwoArray[0] + topEntry.getWidth();
- int height = topEntry.getIntrinsicHeight();
+ int maxX = mTmpTwoArray[0] + topRow.getWidth();
+ int height = topRow.getIntrinsicHeight();
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index be4df45..9c1c71a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -84,8 +84,8 @@
// We might touch above the visible heads up child, but then we still would
// like to capture it.
NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
- if (topEntry != null && topEntry.row.isPinned()) {
- mPickedChild = topEntry.row;
+ if (topEntry != null && topEntry.isRowPinned()) {
+ mPickedChild = topEntry.getRow();
mTouchingHeadsUpView = true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index c0d1818..7d13679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -227,6 +227,7 @@
mShowingSoon = false;
if (mExpansion == EXPANSION_VISIBLE) {
mKeyguardView.onResume();
+ mKeyguardView.resetSecurityContainer();
}
StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN);
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/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/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 0cf1b3d..8657003 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -56,7 +56,6 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
-import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -863,16 +862,9 @@
public static View create(Context context, FragmentListener listener) {
final int displayId = context.getDisplay().getDisplayId();
- final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- final int height = isDefaultDisplay
- ? LayoutParams.MATCH_PARENT
- : context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, height,
- // TODO(b/117478341): Resolve one status bar/ navigation bar assumption
- isDefaultDisplay
- ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
- : WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -884,10 +876,6 @@
lp.setTitle("NavigationBar" + displayId);
lp.accessibilityTitle = context.getString(R.string.nav_bar);
lp.windowAnimations = 0;
- if (!isDefaultDisplay) {
- lp.flags |= LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
- lp.gravity = Gravity.BOTTOM;
- }
View navigationBarView = LayoutInflater.from(context).inflate(
R.layout.navigation_bar_window, null);
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 2f58ca1..33d022c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -77,6 +77,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 +148,8 @@
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
+ private NavigationPrototypeController mPrototypeController;
+ private NavigationGestureAction[] mDefaultGestureMap;
private QuickScrubAction mQuickScrubAction;
private QuickStepAction mQuickStepAction;
private NavigationBackAction mBackAction;
@@ -261,6 +265,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 +325,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 +347,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;
}
}
@@ -904,6 +952,7 @@
boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
int navBarPos = 0;
try {
+ // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get nav bar position.", e);
@@ -1042,6 +1091,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/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 c74514e..3e31fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -198,7 +198,7 @@
alertNotificationWhenPossible(entry, getActiveAlertManager());
} else {
// The transfer is no longer valid. Free the content.
- entry.row.freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
+ entry.getRow().freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
}
}
}
@@ -299,9 +299,9 @@
Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
if (child != null) {
- if (child.row.keepInParent()
- || child.row.isRemoved()
- || child.row.isDismissed()) {
+ if (child.getRow().keepInParent()
+ || child.isRowRemoved()
+ || child.isRowDismissed()) {
// The notification is actually already removed. No need to alert it.
return;
}
@@ -390,10 +390,10 @@
private void alertNotificationWhenPossible(@NonNull Entry entry,
@NonNull AlertingNotificationManager alertManager) {
@InflationFlag int contentFlag = alertManager.getContentFlag();
- if (!entry.row.isInflationFlagSet(contentFlag)) {
+ if (!entry.getRow().isInflationFlagSet(contentFlag)) {
mPendingAlerts.put(entry.key, new PendingAlertInfo(entry, alertManager));
- entry.row.updateInflationFlag(contentFlag, true /* shouldInflate */);
- entry.row.inflateViews();
+ entry.getRow().updateInflationFlag(contentFlag, true /* shouldInflate */);
+ entry.getRow().inflateViews();
return;
}
if (alertManager.isAlerting(entry.key)) {
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 8ceabf8..448b5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -87,8 +87,7 @@
group.expanded = expanded;
if (group.summary != null) {
for (OnGroupChangeListener listener : mListeners) {
- listener.onGroupExpansionChanged(group.summary.row,
- expanded);
+ listener.onGroupExpansionChanged(group.summary.getRow(), expanded);
}
}
}
@@ -133,7 +132,7 @@
}
public void onEntryAdded(final NotificationData.Entry added) {
- if (added.row.isRemoved()) {
+ if (added.isRowRemoved()) {
added.setDebugThrowable(new Throwable());
}
final StatusBarNotification sbn = added.notification;
@@ -152,17 +151,17 @@
if (existing != null && existing != added) {
Throwable existingThrowable = existing.getDebugThrowable();
Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
- + "existing removed: " + existing.row.isRemoved()
+ + "existing removed: " + existing.isRowRemoved()
+ (existingThrowable != null
? Log.getStackTraceString(existingThrowable) + "\n": "")
- + " added removed" + added.row.isRemoved()
+ + " added removed" + added.isRowRemoved()
, new Throwable());
}
group.children.put(added.key, added);
updateSuppression(group);
} else {
group.summary = added;
- group.expanded = added.row.areChildrenExpanded();
+ group.expanded = added.areChildrenExpanded();
updateSuppression(group);
if (!group.children.isEmpty()) {
ArrayList<NotificationData.Entry> childrenCopy
@@ -263,9 +262,9 @@
if (!isOnlyChild(sbn)) {
return false;
}
- ExpandableNotificationRow logicalGroupSummary = getLogicalGroupSummary(sbn);
+ NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn);
return logicalGroupSummary != null
- && !logicalGroupSummary.getStatusBarNotification().equals(sbn);
+ && !logicalGroupSummary.notification.equals(sbn);
}
private int getTotalNumberOfChildren(StatusBarNotification sbn) {
@@ -339,7 +338,7 @@
* Get the summary of a specified status bar notification. For isolated notification this return
* itself.
*/
- public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) {
+ public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) {
return getGroupSummary(getGroupKey(sbn));
}
@@ -348,16 +347,17 @@
* but the logical summary, i.e when a child is isolated, it still returns the summary as if
* it wasn't isolated.
*/
- public ExpandableNotificationRow getLogicalGroupSummary(StatusBarNotification sbn) {
+ public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) {
return getGroupSummary(sbn.getGroupKey());
}
@Nullable
- private ExpandableNotificationRow getGroupSummary(String groupKey) {
+ private NotificationData.Entry getGroupSummary(String groupKey) {
NotificationGroup group = mGroupMap.get(groupKey);
+ //TODO: see if this can become an Entry
return group == null ? null
: group.summary == null ? null
- : group.summary.row;
+ : group.summary;
}
/**
@@ -438,11 +438,11 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
}
@Override
@@ -533,8 +533,7 @@
private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
return notificationGroup.summary == null
- || notificationGroup.summary.row.getClipTopAmount() > 0
- || notificationGroup.summary.row.getTranslationY() < 0;
+ || notificationGroup.summary.isGroupNotFullyVisible();
}
public void setHeadsUpManager(HeadsUpManager headsUpManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 184766c..2d5d562 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -192,13 +192,13 @@
&& !mEntryManager.getNotificationData().isHighPriority(entry.notification)) {
return false;
}
- if (!StatusBar.isTopLevelChild(entry)) {
+ if (!entry.isTopLevelChild()) {
return false;
}
- if (entry.row.getVisibility() == View.GONE) {
+ if (entry.getRow().getVisibility() == View.GONE) {
return false;
}
- if (entry.row.isDismissed() && hideDismissed) {
+ if (entry.isRowDismissed() && hideDismissed) {
return false;
}
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 851e6d0..33d176a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2500,25 +2500,26 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
- mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true);
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
+ mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
// When we're unpinning the notification via active edge they remain heads-upped,
// we need to make sure that an animation happens in this case, otherwise the notification
// will stick to the top without any interaction.
- if (isFullyCollapsed() && headsUp.isHeadsUp()) {
- mNotificationStackScroller.generateHeadsUpAnimation(headsUp, false);
- headsUp.setHeadsUpIsVisible();
+ if (isFullyCollapsed() && entry.isRowHeadsUp()) {
+ mNotificationStackScroller.generateHeadsUpAnimation(
+ entry.getHeadsUpAnimationView(), false);
+ entry.setHeadsUpIsVisible();
}
}
@Override
public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
- mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
+ mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
}
@Override
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..497fdfb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -30,9 +30,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 +100,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 +108,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 +142,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);
}
}
}
@@ -382,6 +384,7 @@
action.onLayout(changed, left, top, right, bottom);
}
}
+ mLastLayoutRect.set(left, top, right, bottom);
}
@Override
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 45e924f..408ab42 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;
@@ -195,7 +196,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;
@@ -484,7 +484,7 @@
private Runnable mLaunchTransitionEndRunnable;
protected boolean mLaunchTransitionFadingAway;
- private ExpandableNotificationRow mDraggedDownRow;
+ private NotificationData.Entry mDraggedDownEntry;
private boolean mLaunchCameraOnScreenTurningOn;
private boolean mLaunchCameraOnFinishedGoingToSleep;
private int mLastCameraLaunchSource;
@@ -721,7 +721,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 +843,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);
@@ -1061,13 +1052,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();
}
/**
@@ -1265,10 +1267,6 @@
mQSPanel.clickTile(tile);
}
- public static boolean isTopLevelChild(Entry entry) {
- return entry.row.getParent() instanceof NotificationStackScrollLayout;
- }
-
public boolean areNotificationsHidden() {
return mZenController.areNotificationsHiddenInShade();
}
@@ -1491,12 +1489,12 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
dismissVolumeDialog();
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
}
@Override
@@ -2251,7 +2249,8 @@
private void notifyUiVisibilityChanged(int vis) {
try {
if (mLastDispatchedSystemUiVisibility != vis) {
- mWindowManagerService.statusBarVisibilityChanged(vis);
+ // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
+ mWindowManagerService.statusBarVisibilityChanged(Display.DEFAULT_DISPLAY, vis);
mLastDispatchedSystemUiVisibility = vis;
}
} catch (RemoteException ex) {
@@ -2604,9 +2603,7 @@
final int notificationCount = activeNotifications.size();
for (int i = 0; i < notificationCount; i++) {
NotificationData.Entry entry = activeNotifications.get(i);
- if (entry.row != null) {
- entry.row.resetUserExpansion();
- }
+ entry.resetUserExpansion();
}
}
@@ -3037,10 +3034,10 @@
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
updatePanelExpansionForKeyguard();
- if (mDraggedDownRow != null) {
- mDraggedDownRow.setUserLocked(false);
- mDraggedDownRow.notifyHeightChanged(false /* needsAnimation */);
- mDraggedDownRow = null;
+ if (mDraggedDownEntry != null) {
+ mDraggedDownEntry.setUserLocked(false);
+ mDraggedDownEntry.notifyHeightChanged(false /* needsAnimation */);
+ mDraggedDownEntry = null;
}
}
@@ -3180,9 +3177,9 @@
}
long delay = mKeyguardMonitor.calculateGoingToFullShadeDelay();
mNotificationPanel.animateToFullShade(delay);
- if (mDraggedDownRow != null) {
- mDraggedDownRow.setUserLocked(false);
- mDraggedDownRow = null;
+ if (mDraggedDownEntry != null) {
+ mDraggedDownEntry.setUserLocked(false);
+ mDraggedDownEntry = null;
}
// TODO(115978725): Support animations on external nav bars.
@@ -3574,14 +3571,15 @@
int userId = mLockscreenUserManager.getCurrentUserId();
ExpandableNotificationRow row = null;
+ NotificationData.Entry entry = null;
if (expandView instanceof ExpandableNotificationRow) {
- row = (ExpandableNotificationRow) expandView;
- row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
+ entry = ((ExpandableNotificationRow) expandView).getEntry();
+ entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
// Indicate that the group expansion is changing at this time -- this way the group
// and children backgrounds / divider animations will look correct.
- row.setGroupExpansionChanging(true);
- if (row.getStatusBarNotification() != null) {
- userId = row.getStatusBarNotification().getUserId();
+ entry.setGroupExpansionChanging(true);
+ if (entry.notification != null) {
+ userId = entry.notification.getUserId();
}
}
boolean fullShadeNeedsBouncer = !mLockscreenUserManager.
@@ -3591,7 +3589,7 @@
if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
showBouncerIfKeyguard();
- mDraggedDownRow = row;
+ mDraggedDownEntry = entry;
mPendingRemoteInputView = null;
} else {
mNotificationPanel.animateToFullShade(0 /* delay */);
@@ -4325,7 +4323,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());
@@ -4344,6 +4349,9 @@
if (intent.isActivity()) {
mAssistManager.hideAssist();
}
+ if (intentSentCallback != null) {
+ intentSentCallback.run();
+ }
}, afterKeyguardGone);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index edfc049..588c3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -418,7 +418,7 @@
StatusBarNotification parentToCancel = null;
if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
StatusBarNotification summarySbn =
- mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
+ mGroupManager.getLogicalGroupSummary(sbn).notification;
if (shouldAutoCancel(summarySbn)) {
parentToCancel = summarySbn;
}
@@ -591,7 +591,7 @@
public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
- mShadeController.goToLockedShade(clickedEntry.row);
+ mShadeController.goToLockedShade(clickedEntry.getRow());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index fdab616..e7280643 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -123,15 +123,15 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setEntryPinned: " + isPinned);
}
- ExpandableNotificationRow row = headsUpEntry.mEntry.row;
- if (row.isPinned() != isPinned) {
- row.setPinned(isPinned);
+ NotificationData.Entry entry = headsUpEntry.mEntry;
+ if (entry.isRowPinned() != isPinned) {
+ entry.setRowPinned(isPinned);
updatePinnedMode();
for (OnHeadsUpChangedListener listener : mListeners) {
if (isPinned) {
- listener.onHeadsUpPinned(row);
+ listener.onHeadsUpPinned(entry);
} else {
- listener.onHeadsUpUnPinned(row);
+ listener.onHeadsUpUnPinned(entry);
}
}
}
@@ -144,7 +144,7 @@
@Override
protected void onAlertEntryAdded(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setHeadsUp(true);
+ entry.setHeadsUp(true);
setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, true);
@@ -154,12 +154,12 @@
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setHeadsUp(false);
+ entry.setHeadsUp(false);
setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
}
protected void updatePinnedMode() {
@@ -282,7 +282,7 @@
private boolean hasPinnedNotificationInternal() {
for (String key : mAlertEntries.keySet()) {
AlertEntry entry = getHeadsUpEntry(key);
- if (entry.mEntry.row.isPinned()) {
+ if (entry.mEntry.isRowPinned()) {
return true;
}
}
@@ -302,10 +302,9 @@
// when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
// on the screen.
- if (userUnPinned && entry.mEntry != null && entry.mEntry.row != null) {
- ExpandableNotificationRow row = entry.mEntry.row;
- if (row.mustStayOnScreen()) {
- row.setHeadsUpIsVisible();
+ if (userUnPinned && entry.mEntry != null) {
+ if (entry.mEntry.mustStayOnScreen()) {
+ entry.mEntry.setHeadsUpIsVisible();
}
}
}
@@ -341,7 +340,7 @@
*/
public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
- if (headsUpEntry != null && entry.row.isPinned()) {
+ if (headsUpEntry != null && entry.isRowPinned()) {
headsUpEntry.setExpanded(expanded);
}
}
@@ -365,15 +364,15 @@
@Override
protected boolean isSticky() {
- return (mEntry.row.isPinned() && expanded)
+ return (mEntry.isRowPinned() && expanded)
|| remoteInputActive || hasFullScreenIntent(mEntry);
}
@Override
public int compareTo(@NonNull AlertEntry alertEntry) {
HeadsUpEntry headsUpEntry = (HeadsUpEntry) alertEntry;
- boolean isPinned = mEntry.row.isPinned();
- boolean otherPinned = headsUpEntry.mEntry.row.isPinned();
+ boolean isPinned = mEntry.isRowPinned();
+ boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
if (isPinned && !otherPinned) {
return -1;
} else if (!isPinned && otherPinned) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index d434768..7ad547a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -33,12 +33,12 @@
/**
* A notification was just pinned to the top.
*/
- default void onHeadsUpPinned(ExpandableNotificationRow headsUp) {}
+ default void onHeadsUpPinned(NotificationData.Entry entry) {}
/**
* A notification was just unpinned from the top.
*/
- default void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {}
+ default void onHeadsUpUnPinned(NotificationData.Entry entry) {}
/**
* A notification just became a heads up or turned back to its normal state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index a485fa8..866015e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -245,7 +245,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- if (mEntry.row.isChangingPosition()) {
+ if (mEntry.getRow().isChangingPosition()) {
if (getVisibility() == VISIBLE && mEditText.isFocusable()) {
mEditText.requestFocus();
}
@@ -255,7 +255,7 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (mEntry.row.isChangingPosition() || isTemporarilyDetached()) {
+ if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
return;
}
mController.removeRemoteInput(mEntry, mToken);
@@ -495,7 +495,7 @@
}
private void defocusIfNeeded(boolean animate) {
- if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()
+ if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
|| isTemporarilyDetached()) {
if (isTemporarilyDetached()) {
// We might get reattached but then the other one of HUN / expanded might steal
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 42f1378..f36066c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -1,6 +1,8 @@
package com.android.systemui.statusbar.policy;
import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.Context;
@@ -19,6 +21,7 @@
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;
@@ -30,6 +33,7 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -38,14 +42,15 @@
import java.text.BreakIterator;
import java.util.Comparator;
+import java.util.List;
import java.util.PriorityQueue;
-/** View which displays smart reply buttons in notifications. */
+/** View which displays smart reply and smart actions buttons in notifications. */
public class SmartReplyView extends ViewGroup {
private static final String TAG = "SmartReplyView";
- private static final int MEASURE_SPEC_ANY_WIDTH =
+ private static final int MEASURE_SPEC_ANY_LENGTH =
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
private static final Comparator<View> DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR =
@@ -98,6 +103,8 @@
private final int mStrokeWidth;
private final double mMinStrokeContrast;
+ private ActivityStarter mActivityStarter;
+
public SmartReplyView(Context context, AttributeSet attrs) {
super(context, attrs);
mConstants = Dependency.get(SmartReplyConstants.class);
@@ -168,19 +175,28 @@
Math.max(getChildCount(), 1), DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR);
}
- public void setRepliesFromRemoteInput(
- RemoteInput remoteInput, PendingIntent pendingIntent,
- SmartReplyController smartReplyController, NotificationData.Entry entry,
- View smartReplyContainer, CharSequence[] choices) {
- mSmartReplyContainer = smartReplyContainer;
+ /**
+ * Reset the smart suggestions view to allow adding new replies and actions.
+ */
+ public void resetSmartSuggestions(View newSmartReplyContainer) {
+ mSmartReplyContainer = newSmartReplyContainer;
removeAllViews();
mCurrentBackgroundColor = mDefaultBackgroundColor;
- if (remoteInput != null && pendingIntent != null) {
- if (choices != null) {
- for (int i = 0; i < choices.length; ++i) {
+ }
+
+ /**
+ * Add smart replies to this view, using the provided {@link RemoteInput} and
+ * {@link PendingIntent} to respond when the user taps a smart reply. Only the replies that fit
+ * into the notification are shown.
+ */
+ public void addRepliesFromRemoteInput(
+ SmartReplies smartReplies,
+ SmartReplyController smartReplyController, NotificationData.Entry entry) {
+ if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
+ if (smartReplies.choices != null) {
+ for (int i = 0; i < smartReplies.choices.length; ++i) {
Button replyButton = inflateReplyButton(
- getContext(), this, i, choices[i], remoteInput, pendingIntent,
- smartReplyController, entry);
+ getContext(), this, i, smartReplies, smartReplyController, entry);
addView(replyButton);
}
}
@@ -188,6 +204,24 @@
reallocateCandidateButtonQueueForSqueezing();
}
+ /**
+ * 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,
+ 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, n, smartActions, smartReplyController, entry);
+ addView(actionButton);
+ }
+ }
+ reallocateCandidateButtonQueueForSqueezing();
+ }
+
public static SmartReplyView inflate(Context context, ViewGroup root) {
return (SmartReplyView)
LayoutInflater.from(context).inflate(R.layout.smart_reply_view, root, false);
@@ -195,22 +229,25 @@
@VisibleForTesting
Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
- CharSequence choice, RemoteInput remoteInput, PendingIntent pendingIntent,
- SmartReplyController smartReplyController, NotificationData.Entry entry) {
+ SmartReplies smartReplies, SmartReplyController smartReplyController,
+ NotificationData.Entry entry) {
Button b = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_reply_button, root, false);
+ CharSequence choice = smartReplies.choices[replyIndex];
b.setText(choice);
OnDismissAction action = () -> {
- smartReplyController.smartReplySent(entry, replyIndex, b.getText());
+ smartReplyController.smartReplySent(
+ entry, replyIndex, b.getText(), smartReplies.fromAssistant);
Bundle results = new Bundle();
- results.putString(remoteInput.getResultKey(), choice.toString());
+ results.putString(smartReplies.remoteInput.getResultKey(), choice.toString());
Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, intent, results);
+ RemoteInput.addResultsToIntent(new RemoteInput[]{smartReplies.remoteInput}, intent,
+ results);
RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE);
entry.setHasSentReply();
try {
- pendingIntent.send(context, 0, intent);
+ smartReplies.pendingIntent.send(context, 0, intent);
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Unable to send smart reply", e);
}
@@ -234,6 +271,54 @@
return b;
}
+ @VisibleForTesting
+ 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());
+ button.setCompoundDrawables(iconDrawable, null, null, null);
+
+ button.setOnClickListener(view ->
+ getActivityStarter().startPendingIntentDismissingKeyguard(
+ action.actionIntent,
+ () -> smartReplyController.smartActionClicked(
+ entry, actionIndex, action, smartActions.fromAssistant)));
+
+ // TODO(b/119010281): handle accessibility
+
+ 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);
@@ -277,7 +362,7 @@
child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
buttonPaddingHorizontal, child.getPaddingBottom());
- child.measure(MEASURE_SPEC_ANY_WIDTH, heightMeasureSpec);
+ child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
final int lineCount = ((Button) child).getLineCount();
if (lineCount < 1 || lineCount > 2) {
@@ -437,6 +522,18 @@
return (int) Math.ceil(optimalTextWidth);
}
+ /**
+ * Returns the combined width of the left drawable (the action icon) and the padding between the
+ * drawable and the button text.
+ */
+ private int getLeftCompoundDrawableWidthWithPadding(Button button) {
+ Drawable[] drawables = button.getCompoundDrawables();
+ Drawable leftDrawable = drawables[0];
+ if (leftDrawable == null) return 0;
+
+ return leftDrawable.getBounds().width() + button.getCompoundDrawablePadding();
+ }
+
private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) {
int oldWidth = button.getMeasuredWidth();
if (button.getPaddingLeft() != mDoubleLineButtonPaddingHorizontal) {
@@ -449,7 +546,8 @@
button.setPadding(mDoubleLineButtonPaddingHorizontal, button.getPaddingTop(),
mDoubleLineButtonPaddingHorizontal, button.getPaddingBottom());
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
- 2 * mDoubleLineButtonPaddingHorizontal + textWidth, MeasureSpec.AT_MOST);
+ 2 * mDoubleLineButtonPaddingHorizontal + textWidth
+ + getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
button.measure(widthMeasureSpec, heightMeasureSpec);
final int newWidth = button.getMeasuredWidth();
@@ -607,6 +705,13 @@
button.setTextColor(textColor);
}
+ private ActivityStarter getActivityStarter() {
+ if (mActivityStarter == null) {
+ mActivityStarter = Dependency.get(ActivityStarter.class);
+ }
+ return mActivityStarter;
+ }
+
@VisibleForTesting
static class LayoutParams extends ViewGroup.LayoutParams {
@@ -646,4 +751,40 @@
return show;
}
}
+
+ /**
+ * Data class for smart replies.
+ */
+ public static class SmartReplies {
+ @NonNull
+ public final RemoteInput remoteInput;
+ @NonNull
+ public final PendingIntent pendingIntent;
+ @NonNull
+ public final CharSequence[] choices;
+ public final boolean fromAssistant;
+
+ public SmartReplies(CharSequence[] choices, RemoteInput remoteInput,
+ PendingIntent pendingIntent, boolean fromAssistant) {
+ this.choices = choices;
+ this.remoteInput = remoteInput;
+ this.pendingIntent = pendingIntent;
+ this.fromAssistant = fromAssistant;
+ }
+ }
+
+
+ /**
+ * Data class for smart actions.
+ */
+ public static class SmartActions {
+ @NonNull
+ public final List<Notification.Action> actions;
+ public final boolean fromAssistant;
+
+ public SmartActions(List<Notification.Action> actions, boolean fromAssistant) {
+ this.actions = actions;
+ this.fromAssistant = fromAssistant;
+ }
+ }
}
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/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index a26b1b5..0953951 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -19,11 +19,9 @@
import static org.mockito.ArgumentMatchers.any;
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.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.content.ContentResolver;
@@ -34,10 +32,15 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.Log;
+
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.SliceProvider;
+import androidx.slice.SliceSpecs;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.core.SliceQuery;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ZenModeController;
import org.junit.Assert;
import org.junit.Before;
@@ -50,13 +53,6 @@
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
-import androidx.slice.Slice;
-import androidx.slice.SliceItem;
-import androidx.slice.SliceProvider;
-import androidx.slice.SliceSpecs;
-import androidx.slice.builders.ListBuilder;
-import androidx.slice.core.SliceQuery;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -164,7 +160,7 @@
}
@Override
- protected boolean isDndSuppressingNotifications() {
+ protected boolean isDndOn() {
return mIsZenMode;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index b44630a..c28e74e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -32,12 +32,14 @@
import android.content.Intent;
import android.os.BatteryManager;
import android.os.HardwarePropertiesManager;
+import android.os.IThermalService;
import android.os.PowerManager;
import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestableResources;
-import android.test.suitebuilder.annotation.SmallTest;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
@@ -45,15 +47,16 @@
import com.android.systemui.power.PowerUI.WarningsUI;
import com.android.systemui.statusbar.phone.StatusBar;
-import java.time.Duration;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -76,6 +79,7 @@
private PowerUI mPowerUI;
private EnhancedEstimates mEnhancedEstimates;
@Mock private PowerManager mPowerManager;
+ @Mock private IThermalService mThermalServiceMock;
@Before
public void setup() {
@@ -124,6 +128,8 @@
resources.addOverride(R.integer.config_warningTemperature, 55);
mPowerUI.start();
+ // Guarantees mHandler has processed all messages.
+ TestableLooper.get(this).processAllMessages();
verify(mMockWarnings).showHighTemperatureWarning();
}
@@ -136,6 +142,8 @@
resources.addOverride(R.integer.config_warningTemperature, 55);
mPowerUI.start();
+ // Guarantees mHandler has processed all messages.
+ TestableLooper.get(this).processAllMessages();
verify(mMockWarnings).showHighTemperatureWarning();
}
@@ -541,5 +549,6 @@
mPowerUI = new PowerUI();
mPowerUI.mContext = mContext;
mPowerUI.mComponents = mContext.getComponents();
+ mPowerUI.mThermalService = mThermalServiceMock;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 48491d7..24bcca50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -16,8 +16,12 @@
package com.android.systemui.privacy
+import android.app.ActivityManager
import android.app.AppOpsManager
+import android.content.Intent
import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
import android.support.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -34,9 +38,11 @@
import org.mockito.ArgumentMatchers.anyList
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
-
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -44,10 +50,18 @@
@RunWithLooper
class PrivacyItemControllerTest : SysuiTestCase() {
+ companion object {
+ val CURRENT_USER_ID = ActivityManager.getCurrentUser()
+ val OTHER_USER = UserHandle(CURRENT_USER_ID + 1)
+ const val TAG = "PrivacyItemControllerTest"
+ }
+
@Mock
private lateinit var appOpsController: AppOpsController
@Mock
private lateinit var callback: PrivacyItemController.Callback
+ @Mock
+ private lateinit var userManager: UserManager
private lateinit var testableLooper: TestableLooper
private lateinit var privacyItemController: PrivacyItemController
@@ -57,15 +71,17 @@
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- appOpsController = mDependency.injectMockDependency(AppOpsController:: class.java)
+ appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
+ mContext.addMockSystemService(UserManager::class.java, userManager)
doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0)))
.`when`(appOpsController).getActiveAppOpsForUser(anyInt())
privacyItemController = PrivacyItemController(mContext, callback)
}
+
@Test
fun testSetListeningTrue() {
privacyItemController.setListening(true)
@@ -80,6 +96,38 @@
privacyItemController.setListening(true)
privacyItemController.setListening(false)
verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
- any(AppOpsController.Callback:: class.java))
+ any(AppOpsController.Callback::class.java))
+ }
+
+ @Test
+ fun testRegisterReceiver_allUsers() {
+ val spiedContext = spy(mContext)
+ val itemController = PrivacyItemController(spiedContext, callback)
+
+ verify(spiedContext, atLeastOnce()).registerReceiverAsUser(
+ eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null),
+ eq(null))
+ verify(spiedContext, never()).unregisterReceiver(eq(itemController.userSwitcherReceiver))
+ }
+
+ @Test
+ fun testReceiver_ACTION_USER_FOREGROUND() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_USER_FOREGROUND))
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_ADDED))
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
+ verify(userManager).getProfiles(anyInt())
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index f49c5b4..9d0556f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -120,7 +120,7 @@
mTestHandler = Handler.createAsync(Looper.myLooper());
mSbn = createNewNotification(0 /* id */);
mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mAlertingNotificationManager = createAlertingNotificationManager();
}
@@ -171,7 +171,7 @@
for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
StatusBarNotification sbn = createNewNotification(i);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- entry.row = mRow;
+ entry.setRow(mRow);
mAlertingNotificationManager.showNotification(entry);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index d409e2b..b5d305d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -78,7 +78,7 @@
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, new Notification(), UserHandle.CURRENT, null, 0);
mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mCallback,
mDelegate, mController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index aca1f90..9bed59b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -227,7 +227,7 @@
null /* overrideGroupKey */,
System.currentTimeMillis());
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- entry.row = row;
+ entry.setRow(row);
entry.createIcons(mContext, sbn);
entry.channel = new NotificationChannel(
notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
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 602e613..72d6cd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -37,6 +37,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -73,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() {
@@ -95,7 +96,7 @@
private NotificationData.Entry createEntry() throws Exception {
ExpandableNotificationRow row = mHelper.createRow();
NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
- entry.row = row;
+ entry.setRow(row);
return entry;
}
@@ -108,9 +109,9 @@
NotificationData.Entry entry2 = createEntry();
// Set up the prior state to look like three top level notifications.
- mListContainer.addContainerView(entry0.row);
- mListContainer.addContainerView(entry1.row);
- mListContainer.addContainerView(entry2.row);
+ mListContainer.addContainerView(entry0.getRow());
+ mListContainer.addContainerView(entry1.getRow());
+ mListContainer.addContainerView(entry2.getRow());
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1, entry2));
@@ -118,15 +119,15 @@
when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(true);
when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(true);
- when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0.row);
- when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0.row);
+ when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0);
+ when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0);
// Run updateNotifications - the view hierarchy should be reorganized.
mViewHierarchyManager.updateNotificationViews();
- verify(mListContainer).notifyGroupChildAdded(entry1.row);
- verify(mListContainer).notifyGroupChildAdded(entry2.row);
- assertTrue(Lists.newArrayList(entry0.row).equals(mListContainer.mRows));
+ verify(mListContainer).notifyGroupChildAdded(entry1.getRow());
+ verify(mListContainer).notifyGroupChildAdded(entry2.getRow());
+ assertTrue(Lists.newArrayList(entry0.getRow()).equals(mListContainer.mRows));
}
@Test
@@ -135,11 +136,11 @@
NotificationData.Entry entry0 = createEntry();
NotificationData.Entry entry1 = createEntry();
NotificationData.Entry entry2 = createEntry();
- entry0.row.addChildNotification(entry1.row);
- entry0.row.addChildNotification(entry2.row);
+ entry0.getRow().addChildNotification(entry1.getRow());
+ entry0.getRow().addChildNotification(entry2.getRow());
// Set up the prior state to look like one top level notification.
- mListContainer.addContainerView(entry0.row);
+ mListContainer.addContainerView(entry0.getRow());
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1, entry2));
@@ -152,10 +153,12 @@
mViewHierarchyManager.updateNotificationViews();
verify(mListContainer).notifyGroupChildRemoved(
- entry1.row, entry0.row.getChildrenContainer());
+ entry1.getRow(), entry0.getRow().getChildrenContainer());
verify(mListContainer).notifyGroupChildRemoved(
- entry2.row, entry0.row.getChildrenContainer());
- assertTrue(Lists.newArrayList(entry0.row, entry1.row, entry2.row).equals(mListContainer.mRows));
+ entry2.getRow(), entry0.getRow().getChildrenContainer());
+ assertTrue(
+ Lists.newArrayList(entry0.getRow(), entry1.getRow(), entry2.getRow())
+ .equals(mListContainer.mRows));
}
@Test
@@ -163,10 +166,10 @@
// Tests two top level notifications becoming a suppressed summary and a child.
NotificationData.Entry entry0 = createEntry();
NotificationData.Entry entry1 = createEntry();
- entry0.row.addChildNotification(entry1.row);
+ entry0.getRow().addChildNotification(entry1.getRow());
// Set up the prior state to look like a top level notification.
- mListContainer.addContainerView(entry0.row);
+ mListContainer.addContainerView(entry0.getRow());
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1));
@@ -179,23 +182,23 @@
mViewHierarchyManager.updateNotificationViews();
verify(mListContainer).notifyGroupChildRemoved(
- entry1.row, entry0.row.getChildrenContainer());
- assertTrue(Lists.newArrayList(entry0.row, entry1.row).equals(mListContainer.mRows));
- assertEquals(View.GONE, entry0.row.getVisibility());
- assertEquals(View.VISIBLE, entry1.row.getVisibility());
+ entry1.getRow(), entry0.getRow().getChildrenContainer());
+ assertTrue(Lists.newArrayList(entry0.getRow(), entry1.getRow()).equals(mListContainer.mRows));
+ assertEquals(View.GONE, entry0.getRow().getVisibility());
+ assertEquals(View.VISIBLE, entry1.getRow().getVisibility());
}
@Test
public void testUpdateNotificationViews_appOps() throws Exception {
NotificationData.Entry entry0 = createEntry();
- entry0.row = spy(entry0.row);
+ entry0.setRow(spy(entry0.getRow()));
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0));
- mListContainer.addContainerView(entry0.row);
+ mListContainer.addContainerView(entry0.getRow());
mViewHierarchyManager.updateNotificationViews();
- verify(entry0.row, times(1)).showAppOpsIcons(any());
+ verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
}
private class FakeListContainer implements NotificationListContainer {
@@ -206,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() {}
@@ -252,7 +255,7 @@
public void setMaxDisplayedNotifications(int maxNotifications) {}
@Override
- public void snapViewIfNeeded(ExpandableNotificationRow row) {}
+ public void snapViewIfNeeded(Entry entry) {}
@Override
public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
@@ -271,10 +274,10 @@
}
@Override
- public void cleanUpViewState(View view) {}
+ public void cleanUpViewStateForEntry(Entry entry) { }
@Override
- public boolean isInVisibleLocation(ExpandableNotificationRow row) {
+ public boolean isInVisibleLocation(Entry entry) {
return true;
}
@@ -286,5 +289,6 @@
public boolean hasPulsingNotifications() {
return false;
}
+
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 1d977d8..76b992f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -16,18 +16,15 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -38,7 +35,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import org.junit.Before;
import org.junit.Test;
@@ -93,7 +89,7 @@
@Test
public void testSendSmartReply_updatesRemoteInput() {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
// Sending smart reply should make calls to NotificationEntryManager
// to update the notification with reply and spinner.
@@ -103,11 +99,21 @@
@Test
public void testSendSmartReply_logsToStatusBar() throws RemoteException {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
// Check we log the result to the status bar service.
verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
- TEST_CHOICE_INDEX);
+ TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
+ }
+
+
+ @Test
+ public void testSendSmartReply_logsToStatusBar_generatedByAssistant() throws RemoteException {
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true);
+
+ // Check we log the result to the status bar service.
+ verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
+ TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true);
}
@Test
@@ -121,14 +127,14 @@
@Test
public void testSendSmartReply_reportsSending() {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
assertTrue(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
}
@Test
public void testSendingSmartReply_afterRemove_shouldReturnFalse() {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
mSmartReplyController.stopSending(mEntry);
assertFalse(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
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 f59bfae..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
@@ -39,6 +39,7 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
+import android.app.Person;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -120,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
@@ -421,6 +422,33 @@
assertEquals(snoozeCriterions, entry.snoozeCriteria);
}
+ @Test
+ public void notificationDataEntry_testIsLastMessageFromReply() {
+ Person.Builder person = new Person.Builder()
+ .setName("name")
+ .setKey("abc")
+ .setUri("uri")
+ .setBot(true);
+
+ // EXTRA_MESSAGING_PERSON is the same Person as the sender in last message in EXTRA_MESSAGES
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person.build());
+ Bundle[] messagesBundle = new Bundle[]{ new Notification.MessagingStyle.Message(
+ "text", 0, person.build()).toBundle() };
+ bundle.putParcelableArray(Notification.EXTRA_MESSAGES, messagesBundle);
+
+ Notification notification = new Notification.Builder(mContext, "test")
+ .addExtras(bundle)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+ notification, mContext.getUser(), "", 0);
+
+ NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.setHasSentReply();
+
+ assertTrue(entry.isLastMessageFromReply());
+ }
+
private void initStatusBarNotification(boolean allowDuringSetup) {
Bundle bundle = new Bundle();
bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index d1fe5af..8706e21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -262,14 +262,14 @@
NotificationData.Entry.class);
verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
NotificationData.Entry entry = entryCaptor.getValue();
- verify(mRemoteInputManager).bindRow(entry.row);
+ verify(mRemoteInputManager).bindRow(entry.getRow());
// Row content inflation:
verify(mCallback).onNotificationAdded(entry);
verify(mPresenter).updateNotificationViews();
assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
- assertNotNull(entry.row);
+ assertNotNull(entry.getRow());
assertEquals(mEntry.userSentiment,
NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@@ -294,7 +294,7 @@
verify(mPresenter).updateNotificationViews();
verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
verify(mCallback).onNotificationUpdated(mSbn);
- assertNotNull(mEntry.row);
+ assertNotNull(mEntry.getRow());
assertEquals(mEntry.userSentiment,
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@@ -303,7 +303,7 @@
public void testRemoveNotification() throws Exception {
com.android.systemui.util.Assert.isNotMainThread();
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -313,7 +313,7 @@
verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
verify(mForegroundServiceController).removeNotification(mSbn);
- verify(mListContainer).cleanUpViewState(mRow);
+ verify(mListContainer).cleanUpViewStateForEntry(mEntry);
verify(mPresenter).updateNotificationViews();
verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
verify(mRow).setRemoved();
@@ -332,7 +332,7 @@
extenders.clear();
extenders.add(extender);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -347,7 +347,7 @@
when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
.thenReturn(mEntry.key);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
mEntryManager.updateNotificationsForAppOp(
@@ -372,7 +372,7 @@
@Test
public void testAddNotificationExistingAppOps() {
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
ArraySet<Integer> expected = new ArraySet<>();
expected.add(3);
@@ -395,7 +395,7 @@
@Test
public void testAdd_noExistingAppOps() {
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
when(mForegroundServiceController.getStandardLayoutKey(
mEntry.notification.getUserId(),
@@ -409,7 +409,7 @@
@Test
public void testAdd_existingAppOpsNotForegroundNoti() {
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
ArraySet<Integer> ops = new ArraySet<>();
ops.add(3);
@@ -431,7 +431,7 @@
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntry.setInflationTask(mAsyncInflationTask);
mEntryManager.getNotificationData().add(mEntry);
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
@@ -447,7 +447,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
setSmartActions(mEntry.key, null);
@@ -461,7 +461,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = null;
+ mEntry.setRow(null);
mEntryManager.getNotificationData().add(mEntry);
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
@@ -476,7 +476,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = null;
+ mEntry.setRow(null);
mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index ffb1c2d..f190979 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -47,7 +47,7 @@
public void setUp() {
mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
mEntry = new NotificationData.Entry(mock(StatusBarNotification.class));
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 1c7a8e8..3710fa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -89,7 +89,7 @@
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, new Notification(), UserHandle.CURRENT, null, 0);
mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mLogger = new TestableNotificationLogger(mBarService);
mLogger.setUpWithContainer(mListContainer);
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 c189c95..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
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.notification.row;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doNothing;
@@ -28,29 +31,62 @@
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.service.notification.StatusBarNotification;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.ArraySet;
+import android.util.Pair;
import android.view.NotificationHeaderView;
import android.view.View;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NotificationContentViewTest extends SysuiTestCase {
+ private static final String TEST_ACTION = "com.android.SMART_REPLY_VIEW_ACTION";
+
NotificationContentView mView;
+ @Mock
+ SmartReplyConstants mSmartReplyConstants;
+ @Mock
+ StatusBarNotification mStatusBarNotification;
+ @Mock
+ Notification mNotification;
+ NotificationData.Entry mEntry;
+ @Mock
+ RemoteInput mRemoteInput;
+ @Mock
+ RemoteInput mFreeFormRemoteInput;
+
+ private Icon mActionIcon;
+
+
@Before
@UiThreadTest
public void setup() {
+ MockitoAnnotations.initMocks(this);
+
mView = new NotificationContentView(mContext, null);
ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null);
ExpandableNotificationRow mockRow = spy(row);
@@ -67,6 +103,13 @@
mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+ // Smart replies and actions
+ when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
+ when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
+ mEntry = new NotificationData.Entry(mStatusBarNotification);
+ when(mSmartReplyConstants.isEnabled()).thenReturn(true);
+ mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
}
private View createViewWithHeight(int height) {
@@ -82,7 +125,7 @@
mView.setDark(true, false, 0);
mView.setDark(false, true, 0);
mView.setHeadsUpAnimatingAway(true);
- Assert.assertFalse(mView.isAnimatingVisibleType());
+ assertFalse(mView.isAnimatingVisibleType());
}
@Test
@@ -115,4 +158,189 @@
verify(mockAmbient, never()).showAppOpsIcons(ops);
verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
}
+
+ private void setupAppGeneratedReplies(CharSequence[] smartReplyTitles) {
+ Notification.Action freeFormAction =
+ new Notification.Action.Builder(null, "Freeform Test Action", null).build();
+ setupAppGeneratedReplies(smartReplyTitles, freeFormAction);
+ }
+
+ private void setupAppGeneratedReplies(
+ CharSequence[] smartReplyTitles,
+ Notification.Action freeFormRemoteInputAction) {
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0);
+ Notification.Action action =
+ new Notification.Action.Builder(null, "Test Action", pendingIntent).build();
+ when(mRemoteInput.getChoices()).thenReturn(smartReplyTitles);
+ Pair<RemoteInput, Notification.Action> remoteInputActionPair =
+ Pair.create(mRemoteInput, action);
+ when(mNotification.findRemoteInputActionPair(false)).thenReturn(remoteInputActionPair);
+
+ Pair<RemoteInput, Notification.Action> freeFormRemoteInputActionPair =
+ Pair.create(mFreeFormRemoteInput, freeFormRemoteInputAction);
+ when(mNotification.findRemoteInputActionPair(true)).thenReturn(
+ freeFormRemoteInputActionPair);
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartReplies() {
+ setupAppGeneratedReplies(new String[] {"Reply1", "Reply2"});
+ when(mSmartReplyConstants.isEnabled()).thenReturn(false);
+
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies).isNull();
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_appGeneratedSmartReplies() {
+ CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
+ setupAppGeneratedReplies(smartReplies);
+ when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+ assertThat(repliesAndActions.smartActions).isNull();
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() {
+ CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
+ setupAppGeneratedReplies(smartReplies);
+ when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+ List<Notification.Action> smartActions =
+ createActions(new String[] {"Test Action 1", "Test Action 2"});
+ when(mNotification.getContextualActions()).thenReturn(smartActions);
+
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+ assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions);
+ assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_sysGeneratedSmartReplies() {
+ Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+ .setAllowGeneratedReplies(true)
+ .build();
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // replies.
+ setupAppGeneratedReplies(null, freeFormAction);
+
+ mEntry.smartReplies =
+ new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.smartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
+ assertThat(repliesAndActions.smartActions).isNull();
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed() {
+ Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+ .setAllowGeneratedReplies(false)
+ .build();
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // replies.
+ setupAppGeneratedReplies(null, freeFormAction);
+
+ mEntry.smartReplies =
+ new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies).isNull();
+ assertThat(repliesAndActions.smartActions).isNull();
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_sysGeneratedSmartActions() {
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // actions.
+ setupAppGeneratedReplies(null);
+
+ mEntry.systemGeneratedSmartActions =
+ createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies).isNull();
+ assertThat(repliesAndActions.smartActions.actions)
+ .isEqualTo(mEntry.systemGeneratedSmartActions);
+ assertThat(repliesAndActions.smartActions.fromAssistant).isTrue();
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_appGenPreferredOverSysGen() {
+ Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+ .setAllowGeneratedReplies(true)
+ .build();
+ CharSequence[] appGenSmartReplies = new String[] {"Reply1", "Reply2"};
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // replies.
+ setupAppGeneratedReplies(appGenSmartReplies, freeFormAction);
+ when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+ List<Notification.Action> appGenSmartActions =
+ createActions(new String[] {"Test Action 1", "Test Action 2"});
+ when(mNotification.getContextualActions()).thenReturn(appGenSmartActions);
+
+ mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ mEntry.systemGeneratedSmartActions =
+ createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(appGenSmartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+ assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions);
+ assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() {
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // actions.
+ setupAppGeneratedReplies(null);
+
+ when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
+
+ mEntry.systemGeneratedSmartActions =
+ createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartActions).isNull();
+ assertThat(repliesAndActions.smartReplies).isNull();
+ }
+
+ private Notification.Action.Builder createActionBuilder(String actionTitle) {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(TEST_ACTION), 0);
+ return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent);
+ }
+
+ private Notification.Action createAction(String actionTitle) {
+ return createActionBuilder(actionTitle).build();
+ }
+
+ private List<Notification.Action> createActions(String[] actionTitles) {
+ List<Notification.Action> actions = new ArrayList<>();
+ for (String title : actionTitles) {
+ actions.add(createAction(title));
+ }
+ return actions;
+ }
}
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 3d2ea70..84bfae6 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;
@@ -62,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;
@@ -177,10 +175,17 @@
NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
ExpandableNotificationRow row = spy(realRow);
+
when(row.getWindowToken()).thenReturn(new Binder());
when(row.getGuts()).thenReturn(guts);
doNothing().when(row).inflateGuts();
+ NotificationData.Entry realEntry = realRow.getEntry();
+ NotificationData.Entry entry = spy(realEntry);
+
+ when(entry.getRow()).thenReturn(row);
+ when(entry.getGuts()).thenReturn(guts);
+
mGutsManager.openGuts(row, 0, 0, menuItem);
mTestableLooper.processAllMessages();
verify(guts).openControls(
@@ -190,13 +195,19 @@
anyBoolean(),
any(Runnable.class));
+ // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
+ verify(row).setGutsView(any());
+
row.onDensityOrFontScaleChanged();
- mGutsManager.onDensityOrFontScaleChanged(row);
+ mGutsManager.onDensityOrFontScaleChanged(entry);
+
mTestableLooper.processAllMessages();
mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+
+ // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
verify(row, times(2)).setGutsView(any());
}
@@ -284,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),
@@ -302,8 +312,7 @@
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
+ eq(0));
}
@Test
@@ -315,8 +324,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),
@@ -333,8 +341,7 @@
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
+ eq(0));
}
@Test
@@ -347,8 +354,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),
@@ -365,8 +371,7 @@
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(true) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
+ eq(0));
}
@Test
@@ -379,8 +384,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),
@@ -397,8 +401,7 @@
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(false) /*isNoisy */,
- eq(IMPORTANCE_DEFAULT),
- eq(NotificationInfo.ACTION_NONE));
+ eq(IMPORTANCE_DEFAULT));
}
@Test
@@ -411,8 +414,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),
@@ -429,8 +431,7 @@
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
+ eq(0));
}
@Test
@@ -442,8 +443,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),
@@ -460,8 +460,7 @@
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_BLOCK));
+ eq(0));
}
@Test
@@ -470,7 +469,7 @@
ExpandableNotificationRow row = spy(createTestNotificationRow());
doReturn(guts).when(row).getGuts();
NotificationData.Entry entry = row.getEntry();
- entry.row = row;
+ entry.setRow(row);
mGutsManager.setExposedGuts(guts);
assertTrue(mGutsManager.shouldExtendLifetime(entry));
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 1cc1c63..3dd493f 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
@@ -134,7 +134,7 @@
.thenReturn(packageInfo);
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = TEST_UID; // non-zero
- when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+ when(mMockPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt())).thenReturn(
applicationInfo);
final PackageInfo systemPackageInfo = new PackageInfo();
systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
@@ -187,7 +187,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);
+ false, 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,16 +200,47 @@
.thenReturn(iconDrawable);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ false, IMPORTANCE_DEFAULT);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@Test
+ public void testBindNotification_noDelegate() throws Exception {
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ false, IMPORTANCE_DEFAULT);
+ final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+ assertEquals(GONE, nameView.getVisibility());
+ final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+ assertEquals(GONE, dividerView.getVisibility());
+ }
+
+ @Test
+ public void testBindNotification_delegate() throws Exception {
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, "other", 0, null, TEST_UID, 0,
+ new Notification(), UserHandle.CURRENT, null, 0);
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = 7; // non-zero
+ when(mMockPackageManager.getApplicationInfo(eq("other"), anyInt())).thenReturn(
+ applicationInfo);
+ when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ false, IMPORTANCE_DEFAULT);
+ final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+ assertEquals(VISIBLE, nameView.getVisibility());
+ assertTrue(nameView.getText().toString().contains("Other"));
+ final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+ assertEquals(VISIBLE, dividerView.getVisibility());
+ }
+
+ @Test
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);
+ false, 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);
@@ -226,7 +257,7 @@
.thenReturn(notificationChannelGroup);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ false, IMPORTANCE_DEFAULT);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -238,7 +269,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);
+ false, IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@@ -247,7 +278,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, false, IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -260,7 +291,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, false, IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -269,7 +300,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);
+ false, IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -278,7 +309,7 @@
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);
+ false, IMPORTANCE_DEFAULT);
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);
@@ -292,7 +323,7 @@
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);
+ true, IMPORTANCE_DEFAULT);
final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
assertEquals(VISIBLE, toggleSilent.getVisibility());
assertEquals(
@@ -304,7 +335,7 @@
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);
+ true, IMPORTANCE_LOW);
final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
assertEquals(VISIBLE, toggleSilent.getVisibility());
assertEquals(
@@ -316,7 +347,7 @@
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);
+ true, IMPORTANCE_DEFAULT);
final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
assertEquals(VISIBLE, toggleSilent.getVisibility());
assertEquals(
@@ -329,7 +360,7 @@
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);
+ true, IMPORTANCE_LOW);
final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
assertEquals(VISIBLE, toggleSilent.getVisibility());
assertEquals(
@@ -341,7 +372,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);
+ false, IMPORTANCE_DEFAULT);
final View block = mNotificationInfo.findViewById(R.id.block);
final View minimize = mNotificationInfo.findViewById(R.id.minimize);
assertEquals(GONE, block.getVisibility());
@@ -356,7 +387,7 @@
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
- }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, true, false, false, IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -368,7 +399,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);
+ false, IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -380,7 +411,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, false, IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -389,11 +420,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);
+ false, 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, false, IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@@ -402,7 +433,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
}
@@ -411,7 +442,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, false, IMPORTANCE_DEFAULT);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
}
@@ -424,7 +455,7 @@
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
latch.countDown();
- }, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, true, true, false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
@@ -437,7 +468,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, false, IMPORTANCE_DEFAULT);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, channelNameView.getVisibility());
@@ -448,7 +479,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, false, IMPORTANCE_DEFAULT);
final TextView blockView = mNotificationInfo.findViewById(R.id.block);
assertEquals(GONE, blockView.getVisibility());
}
@@ -457,7 +488,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, false, 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());
@@ -467,7 +498,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);
+ false, IMPORTANCE_DEFAULT);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -478,7 +509,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);
+ false, IMPORTANCE_DEFAULT);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -489,7 +520,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -503,7 +534,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
mTestableLooper.processAllMessages();
@@ -517,7 +548,7 @@
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);
+ true, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
mTestableLooper.processAllMessages();
@@ -531,7 +562,7 @@
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);
+ true, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
mTestableLooper.processAllMessages();
@@ -545,7 +576,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -560,7 +591,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true, false);
@@ -578,8 +609,8 @@
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 */, false /* isNoisy */, IMPORTANCE_DEFAULT
+ );
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -600,8 +631,8 @@
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 */, false /* isNoisy */, IMPORTANCE_DEFAULT
+ );
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -622,8 +653,7 @@
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -651,8 +681,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 */, false /* isNoisy */, IMPORTANCE_DEFAULT);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -681,7 +710,7 @@
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
false /* isNonblockable */, true /* isForBlockingHelper */,
true, true /* isUserSentimentNegative */, false /* isNoisy */,
- IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
@@ -700,8 +729,7 @@
null /* onSettingsClick */, null /* onAppSettingsClick */,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -724,7 +752,7 @@
true /* isForBlockingHelper */,
true,
false /* isUserSentimentNegative */,
- false /* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ false /* isNoisy */, IMPORTANCE_DEFAULT);
NotificationGuts guts = mock(NotificationGuts.class);
doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
mNotificationInfo.setGutsParent(guts);
@@ -739,7 +767,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -753,7 +781,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -786,7 +814,7 @@
false /* isNonblockable */,
true /* isForBlockingHelper */,
true /* isUserSentimentNegative */,
- false/* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ false/* isNoisy */, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -808,7 +836,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -823,7 +851,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -844,7 +872,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true, false);
@@ -862,7 +890,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -884,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -906,7 +934,7 @@
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);
+ true, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -927,7 +955,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -949,7 +977,7 @@
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);
+ true, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -971,7 +999,7 @@
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);
+ false, IMPORTANCE_LOW);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -992,7 +1020,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1008,7 +1036,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -1025,7 +1053,7 @@
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, false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -1043,8 +1071,8 @@
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, false, IMPORTANCE_DEFAULT
+ );
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -1080,7 +1108,7 @@
TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
(View v, Intent intent) -> {
latch.countDown();
- }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, true, false, false, IMPORTANCE_DEFAULT);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(View.VISIBLE, settingsLink.getVisibility());
settingsLink.performClick();
@@ -1108,7 +1136,7 @@
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
(View v, Intent intent) -> {
latch.countDown();
- }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, true, false, false, IMPORTANCE_DEFAULT);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(View.VISIBLE, settingsLink.getVisibility());
settingsLink.performClick();
@@ -1127,7 +1155,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
- null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ null, true, false, false, IMPORTANCE_DEFAULT);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(GONE, settingsLink.getVisibility());
}
@@ -1148,7 +1176,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ false, IMPORTANCE_DEFAULT);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(GONE, settingsLink.getVisibility());
}
@@ -1171,7 +1199,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
- true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ true, true, false, IMPORTANCE_DEFAULT);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(GONE, settingsLink.getVisibility());
}
@@ -1188,7 +1216,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1201,7 +1229,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -1214,7 +1242,7 @@
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);
+ true, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -1228,7 +1256,7 @@
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);
+ true, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -1242,7 +1270,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -1254,7 +1282,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);
+ false, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -1262,60 +1290,4 @@
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/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/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 74ce5f6..e65e806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -325,7 +325,9 @@
// add notification
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- when(row.isClearable()).thenReturn(true);
+ NotificationData.Entry entry = mock(NotificationData.Entry.class);
+ when(row.getEntry()).thenReturn(entry);
+ when(entry.isClearable()).thenReturn(true);
mStackScroller.addContainerView(row);
mStackScroller.onUpdateRowStates();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 10b0d83..c99e766 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -81,12 +81,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertEquals(mFirst.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
}
@@ -95,12 +95,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertTrue(mHeadsUpAppearanceController.isShown());
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertFalse(mHeadsUpAppearanceController.isShown());
}
@@ -109,12 +109,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 0.0f, 0.0f);
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
}
@@ -125,12 +125,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility());
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertEquals(View.VISIBLE, mOperatorNameView.getVisibility());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 1070795..44deb10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -97,7 +97,7 @@
@Test
public void testCanRemoveImmediately_notTopEntry() {
NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
- laterEntry.row = mRow;
+ laterEntry.setRow(mRow);
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.showNotification(laterEntry);
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/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 96c57f2..c3bc511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -149,7 +149,8 @@
Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
Entry childEntry = mGroupTestHelper.createChildNotification();
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -166,12 +167,14 @@
Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
Entry childEntry = mGroupTestHelper.createChildNotification();
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(true);
mGroupAlertTransferHelper.onInflationFinished(childEntry);
// Alert is immediately removed from summary, and we show child as its content is inflated.
@@ -185,7 +188,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
NotificationData.Entry childEntry2 =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
mHeadsUpManager.showNotification(summaryEntry);
@@ -199,10 +203,12 @@
mGroupManager.onEntryAdded(childEntry2);
// Child entry finishes its inflation.
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(true);
mGroupAlertTransferHelper.onInflationFinished(childEntry);
- verify(childEntry.row, times(1)).freeContentViewWhenSafe(mHeadsUpManager.getContentFlag());
+ verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
+ .getContentFlag());
assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
}
@@ -212,7 +218,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -229,7 +236,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -251,7 +259,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index 1483ae5..b0bd1fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -100,7 +100,7 @@
mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
- assertEquals(summaryEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry.notification));
}
@Test
@@ -143,9 +143,8 @@
// Child entries that are heads upped should be considered separate groups visually even if
// they are the same group logically
- assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
- assertEquals(summaryEntry.row,
- mGroupManager.getLogicalGroupSummary(childEntry.notification));
+ assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
}
@Test
@@ -161,8 +160,7 @@
// Child entries that are heads upped should be considered separate groups visually even if
// they are the same group logically
- assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
- assertEquals(summaryEntry.row,
- mGroupManager.getLogicalGroupSummary(childEntry.notification));
+ assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 01f44fd4..7ad68eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -81,7 +81,7 @@
0 /* postTime */);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- entry.row = row;
+ entry.setRow(row);
when(row.getEntry()).thenReturn(entry);
when(row.getStatusBarNotification()).thenReturn(sbn);
when(row.isInflationFlagSet(anyInt())).thenReturn(true);
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 4534ebe..df7aeab 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
@@ -22,7 +22,9 @@
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,6 +34,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -41,14 +45,14 @@
import android.widget.Button;
import android.widget.LinearLayout;
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-
-import java.util.concurrent.atomic.AtomicReference;
+import com.android.systemui.statusbar.phone.ShadeController;
import org.junit.After;
import org.junit.Before;
@@ -57,6 +61,11 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -67,6 +76,10 @@
private static final String[] TEST_CHOICES = new String[]{"Hello", "What's up?", "I'm here"};
private static final String TEST_NOTIFICATION_KEY = "akey";
+ private static final String[] TEST_ACTION_TITLES = new String[]{
+ "First action", "Open something", "Action"
+ };
+
private static final int WIDTH_SPEC = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
private static final int HEIGHT_SPEC = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
@@ -74,6 +87,8 @@
private SmartReplyView mView;
private View mContainer;
+ private Icon mActionIcon;
+
private int mSingleLinePaddingHorizontal;
private int mDoubleLinePaddingHorizontal;
private int mSpacing;
@@ -82,12 +97,16 @@
private NotificationData.Entry mEntry;
private Notification mNotification;
+ @Mock ActivityStarter mActivityStarter;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mReceiver = new BlockingQueueIntentReceiver();
mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> action.onDismiss());
+ mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
mContainer = new View(mContext, null);
mView = SmartReplyView.inflate(mContext, null);
@@ -108,6 +127,8 @@
when(sbn.getNotification()).thenReturn(mNotification);
when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
mEntry = new NotificationData.Entry(sbn);
+
+ mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
}
@After
@@ -117,7 +138,7 @@
@Test
public void testSendSmartReply_intentContainsResultsAndSource() throws InterruptedException {
- setRepliesFromRemoteInput(TEST_CHOICES);
+ setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
@@ -130,7 +151,7 @@
@Test
public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> {});
- setRepliesFromRemoteInput(TEST_CHOICES);
+ setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
@@ -141,7 +162,7 @@
public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
mDependency.get(KeyguardDismissUtil.class).setDismissHandler(actionRef::set);
- setRepliesFromRemoteInput(TEST_CHOICES);
+ setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
@@ -159,15 +180,24 @@
@Test
public void testSendSmartReply_controllerCalled() {
- setRepliesFromRemoteInput(TEST_CHOICES);
+ setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
- verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2]);
+ verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+ false /* generatedByAsssitant */);
+ }
+
+ @Test
+ public void testSendSmartReply_controllerCalled_generatedByAssistant() {
+ setSmartReplies(TEST_CHOICES, true);
+ mView.getChildAt(2).performClick();
+ verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+ true /* generatedByAsssitant */);
}
@Test
public void testSendSmartReply_hidesContainer() {
mContainer.setVisibility(View.VISIBLE);
- setRepliesFromRemoteInput(TEST_CHOICES);
+ setSmartReplies(TEST_CHOICES);
mView.getChildAt(0).performClick();
assertEquals(View.GONE, mContainer.getVisibility());
}
@@ -198,7 +228,7 @@
ViewGroup expectedView = buildExpectedView(choices, 1);
expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
assertEqualMeasures(expectedView, mView);
@@ -217,7 +247,7 @@
expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
10 + expectedView.getMeasuredHeight());
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
@@ -235,7 +265,7 @@
ViewGroup expectedView = buildExpectedView(choices, 2);
expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
assertEqualMeasures(expectedView, mView);
@@ -254,7 +284,7 @@
expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
10 + expectedView.getMeasuredHeight());
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
@@ -273,7 +303,7 @@
ViewGroup expectedView = buildExpectedView(new CharSequence[]{"Hi", "Bye"}, 1);
expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
assertEqualMeasures(expectedView, mView);
@@ -293,7 +323,7 @@
expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
10 + expectedView.getMeasuredHeight());
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
@@ -313,7 +343,7 @@
new CharSequence[]{"Short", "Short", "Looooooong \nreplyyyyy"}, 2);
expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(
MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
MeasureSpec.UNSPECIFIED);
@@ -335,7 +365,7 @@
expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
10 + expectedView.getMeasuredHeight());
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(
MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
MeasureSpec.UNSPECIFIED);
@@ -359,7 +389,7 @@
expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
10 + expectedView.getMeasuredHeight());
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(
MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
MeasureSpec.UNSPECIFIED);
@@ -371,15 +401,57 @@
assertReplyButtonHidden(mView.getChildAt(2));
}
- private void setRepliesFromRemoteInput(CharSequence[] choices) {
+ private void setSmartReplies(CharSequence[] choices) {
+ setSmartReplies(choices, false);
+ }
+
+ private void setSmartReplies(CharSequence[] choices, boolean fromAssistant) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(TEST_ACTION), 0);
RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
- mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, mContainer, choices);
+ SmartReplyView.SmartReplies smartReplies =
+ new SmartReplyView.SmartReplies(choices, input, pendingIntent, fromAssistant);
+ mView.resetSmartSuggestions(mContainer);
+ mView.addRepliesFromRemoteInput(smartReplies, mLogger, mEntry);
+ }
+
+ private Notification.Action createAction(String actionTitle) {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(TEST_ACTION), 0);
+ return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent).build();
+ }
+
+ private List<Notification.Action> createActions(String[] actionTitles) {
+ List<Notification.Action> actions = new ArrayList<>();
+ for (String title : actionTitles) {
+ actions.add(createAction(title));
+ }
+ return actions;
+ }
+
+ private void setSmartActions(String[] actionTitles) {
+ mView.resetSmartSuggestions(mContainer);
+ 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),
+ mLogger,
+ mEntry);
+ }
+
+ private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
+ return buildExpectedView(choices, lineCount, new ArrayList<>());
}
/** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
- private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
+ private ViewGroup buildExpectedView(
+ CharSequence[] choices, int lineCount, List<Notification.Action> actions) {
LinearLayout layout = new LinearLayout(mContext);
layout.setOrientation(LinearLayout.HORIZONTAL);
@@ -401,10 +473,31 @@
return null;
}
+ // Add smart replies
Button previous = null;
+ SmartReplyView.SmartReplies smartReplies =
+ new SmartReplyView.SmartReplies(choices, null, null, false);
for (int i = 0; i < choices.length; ++i) {
- Button current = mView.inflateReplyButton(mContext, mView, i, choices[i],
- null, null, null, null);
+ Button current = mView.inflateReplyButton(mContext, mView, i, smartReplies,
+ null, null);
+ current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
+ current.getPaddingBottom());
+ if (previous != null) {
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
+ if (isRtl) {
+ lp.leftMargin = mSpacing;
+ } else {
+ lp.rightMargin = mSpacing;
+ }
+ }
+ layout.addView(current);
+ previous = current;
+ }
+
+ // Add smart actions
+ for (int i = 0; i < actions.size(); ++i) {
+ Button current = inflateActionButton(actions.get(i));
current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
current.getPaddingBottom());
if (previous != null) {
@@ -455,4 +548,257 @@
assertEquals(expected.getPaddingRight(), actual.getPaddingRight());
assertEquals(expected.getPaddingBottom(), actual.getPaddingBottom());
}
+
+
+ // =============================================================================================
+ // ============================= Smart Action tests ============================================
+ // =============================================================================================
+
+ @Test
+ public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
+ setSmartActions(TEST_ACTION_TITLES);
+
+ mView.getChildAt(2).performClick();
+
+ verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
+ }
+
+ @Test
+ public void testMeasure_shortSmartActions() {
+ String[] actions = new String[] {"Hi", "Hello", "Bye"};
+ // All choices should be displayed as SINGLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testLayout_shortSmartActions() {
+ String[] actions = new String[] {"Hi", "Hello", "Bye"};
+ // All choices should be displayed as SINGLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+ 10 + expectedView.getMeasuredHeight());
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+ assertEqualLayouts(expectedView, mView);
+ assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_smartActionWithTwoLines() {
+ String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testLayout_smartActionWithTwoLines() {
+ String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+ 10 + expectedView.getMeasuredHeight());
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+ assertEqualLayouts(expectedView, mView);
+ assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_smartActionWithThreeLines() {
+ String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
+
+ // The action with three lines should NOT be displayed. All other actions should be
+ // displayed as SINGLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
+ createActions(new String[]{"Hi", "Bye"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonHidden(mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testLayout_smartActionWithThreeLines() {
+ String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
+
+ // The action with three lines should NOT be displayed. All other actions should be
+ // displayed as SINGLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
+ createActions(new String[]{"Hi", "Bye"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+ 10 + expectedView.getMeasuredHeight());
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+ assertEqualLayouts(expectedView, mView);
+ assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+ // We don't care about mView.getChildAt(1)'s layout because it's hidden (see
+ // testMeasure_smartActionWithThreeLines).
+ assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_squeezeLongestSmartAction() {
+ String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
+ createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartActions(actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testLayout_squeezeLongestSmartAction() {
+ String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
+ createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+ 10 + expectedView.getMeasuredHeight());
+
+ setSmartActions(actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+ mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+ assertEqualLayouts(expectedView, mView);
+ assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_dropLongestSmartAction() {
+ String[] actions = new String[] {"Short", "Short", "LooooooongUnbreakableReplyyyyy"};
+
+ // Short actions should be shown as single line views
+ ViewGroup expectedView = buildExpectedView(
+ new CharSequence[0], 1, createActions(new String[] {"Short", "Short"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+ 10 + expectedView.getMeasuredHeight());
+
+ setSmartActions(actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+ mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+ assertEqualLayouts(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonHidden(mView.getChildAt(2));
+ }
+
+ private Button inflateActionButton(Notification.Action action) {
+ return mView.inflateActionButton(getContext(), mView, 0,
+ new SmartReplyView.SmartActions(Collections.singletonList(action), false),
+ mLogger, mEntry);
+ }
+
+ @Test
+ public void testInflateActionButton_smartActionIconSingleLineSizeForTwoLineButton() {
+ // Ensure smart action icons are the same size regardless of the number of text rows in the
+ // button.
+ Button singleLineButton = inflateActionButton(createAction("One line"));
+ Button doubleLineButton = inflateActionButton(createAction("Two\nlines"));
+ Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable
+ Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable
+ assertEquals(singleLineDrawable.getBounds().width(),
+ doubleLineDrawable.getBounds().width());
+ assertEquals(singleLineDrawable.getBounds().height(),
+ doubleLineDrawable.getBounds().height());
+ }
+
+ @Test
+ public void testMeasure_shortChoicesAndActions() {
+ CharSequence[] choices = new String[] {"Hi", "Hello"};
+ String[] actions = new String[] {"Bye"};
+ // All choices should be displayed as SINGLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(choices, 1, createActions(actions));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_choicesAndActionsSqueezeLongestAction() {
+ CharSequence[] choices = new String[] {"Short", "Short"};
+ String[] actions = new String[] {"Looooooong replyyyyy"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(choices, 2,
+ createActions(new String[] {"Looooooong \nreplyyyyy"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 5c189ce..d5decce 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1832,8 +1832,8 @@
updateMagnificationLocked(userState);
scheduleUpdateFingerprintGestureHandling(userState);
scheduleUpdateInputFilter(userState);
- scheduleUpdateClientsIfNeededLocked(userState);
updateRelevantEventsLocked(userState);
+ scheduleUpdateClientsIfNeededLocked(userState);
updateAccessibilityButtonTargetsLocked(userState);
}
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 17d8ea7..c56f31e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -18,11 +18,13 @@
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
+import static android.util.DebugUtils.flagsToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
import static com.android.server.autofill.Helper.sVerbose;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -72,9 +74,12 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.intelligence.IntelligenceManagerInternal;
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.Arrays;
import java.util.List;
@@ -95,6 +100,27 @@
private static final Object sLock = AutofillManagerService.class;
+
+ /**
+ * IME supports Smart Suggestions.
+ */
+ // NOTE: must be public because of flagsToString()
+ public static final int FLAG_SMART_SUGGESTION_IME = 0x1;
+
+ /**
+ * System supports Smarts Suggestions (as a popup-window similar to standard Autofill).
+ */
+ // NOTE: must be public because of flagsToString()
+ public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x2;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_SMART_SUGGESTION_" }, value = {
+ FLAG_SMART_SUGGESTION_IME,
+ FLAG_SMART_SUGGESTION_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface SmartSuggestionMode {}
+
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
private static final char COMPAT_PACKAGE_DELIMITER = ':';
@@ -102,7 +128,6 @@
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
-
/**
* Maximum number of partitions that can be allowed in a session.
*
@@ -130,6 +155,7 @@
private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
private final LocalService mLocalService = new LocalService();
+ final IntelligenceManagerInternal mIntelligenceManagerInternal;
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -153,13 +179,21 @@
@GuardedBy("mLock")
private boolean mAllowInstantService;
+ /**
+ * Supported modes for Augmented Autofill Smart Suggestions.
+ */
+ @GuardedBy("mLock")
+ private int mSupportedSmartSuggestionModes;
+
public AutofillManagerService(Context context) {
super(context, UserManager.DISALLOW_AUTOFILL);
mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
+ mIntelligenceManagerInternal = LocalServices.getService(IntelligenceManagerInternal.class);
setLogLevelFromSettings();
setMaxPartitionsFromSettings();
setMaxVisibleDatasetsFromSettings();
+ setSmartSuggestionEmulationFromSettings();
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -186,6 +220,9 @@
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, observer,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS), false, observer,
+ UserHandle.USER_ALL);
}
@Override // from AbstractMasterSystemService
@@ -200,6 +237,9 @@
case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
setMaxVisibleDatasetsFromSettings();
break;
+ case Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS:
+ setSmartSuggestionEmulationFromSettings();
+ break;
default:
Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
// fall through
@@ -243,6 +283,10 @@
mUi.hideAll(null);
}
+ @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
+ return mSupportedSmartSuggestionModes;
+ }
+
// Called by Shell command.
void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
Slog.i(TAG, "destroySessions() for userId " + userId);
@@ -420,6 +464,19 @@
}
}
+ private void setSmartSuggestionEmulationFromSettings() {
+ final int flags = Settings.Global.getInt(getContext().getContentResolver(),
+ Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS, 0);
+ if (sDebug) {
+ Slog.d(TAG, "setSmartSuggestionEmulationFromSettings(): "
+ + smartSuggestionFlagsToString(flags));
+ }
+
+ synchronized (mLock) {
+ mSupportedSmartSuggestionModes = flags;
+ }
+ }
+
// Called by Shell command.
void getScore(@Nullable String algorithmName, @NonNull String value1,
@NonNull String value2, @NonNull RemoteCallback callback) {
@@ -610,6 +667,10 @@
}
}
+ static String smartSuggestionFlagsToString(int flags) {
+ return flagsToString(AutofillManagerService.class, "FLAG_SMART_SUGGESTION_", flags);
+ }
+
private final class LocalService extends AutofillManagerInternal {
@Override
public void onBackKeyPressed() {
@@ -1158,6 +1219,10 @@
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));
+ }
if (showHistory) {
pw.println(); pw.println("Requests history:"); pw.println();
mRequestsHistory.reverseDump(fd, pw, args);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 67ccc9b..0df99d4 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -73,6 +73,7 @@
import com.android.server.AbstractPerUserSystemService;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
+import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
import com.android.server.autofill.ui.AutoFillUI;
import java.io.PrintWriter;
@@ -268,8 +269,8 @@
pruneAbandonedSessionsLocked();
final Session newSession = createSessionByTokenLocked(activityToken, taskId, uid,
- appCallbackToken, hasCallback, componentName, compatMode, bindInstantServiceAllowed,
- flags);
+ appCallbackToken, hasCallback, componentName, compatMode,
+ bindInstantServiceAllowed, flags);
if (newSession == null) {
return NO_SESSION;
}
@@ -823,6 +824,12 @@
return true;
}
+ @GuardedBy("mLock")
+ @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
+ // TODO(b/111330312): once we support IME, we need to set it per-user (OR'ed with master)
+ return mMaster.getSupportedSmartSuggestionModesLocked();
+ }
+
@Override
@GuardedBy("mLock")
protected void dumpLocked(String prefix, PrintWriter pw) {
@@ -962,6 +969,9 @@
if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
session.forceRemoveSelfLocked();
}
+ else {
+ session.destroyAugmentedAutofillWindowsLocked();
+ }
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1ff1acd..8676f7f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -25,6 +25,9 @@
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_IME;
+import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_SYSTEM;
+import static com.android.server.autofill.AutofillManagerService.smartSuggestionFlagsToString;
import static com.android.server.autofill.Helper.getNumericValue;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
@@ -93,8 +96,11 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.server.AbstractRemoteService;
+import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.autofill.ui.PendingUi;
+import com.android.server.intelligence.IntelligenceManagerInternal;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -242,6 +248,10 @@
@GuardedBy("mLock")
private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1);
+ @GuardedBy("mLock")
+ @Nullable
+ private AugmentedAutofillCallback mAugmentedAutofillCallback;
+
/**
* Receiver of assist data from the app's {@link Activity}.
*/
@@ -2497,15 +2507,83 @@
processResponseLocked(newResponse, newClientState, 0);
}
+ @GuardedBy("mLock")
private void processNullResponseLocked(int flags) {
- if (sVerbose) Slog.v(TAG, "canceling session " + id + " when server returned null");
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
}
mService.resetLastResponse();
- // Nothing to be done, but need to notify client.
- notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
- removeSelf();
+
+ // The default autofill service cannot fullfill the request, let's check if the intelligence
+ // service can.
+ mAugmentedAutofillCallback = triggerAugmentedAutofillLocked();
+ if (mAugmentedAutofillCallback == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "canceling session " + id + " when server returned null and there is no"
+ + " AugmentedAutofill for user");
+ }
+ // Nothing to be done, but need to notify client.
+ notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
+ removeSelf();
+ } else {
+ // TODO(b/111330312, b/119638958): must set internal state so when user focus other
+ // fields it does not generate a new call to the standard autofill service (right now
+ // it does). Must also add CTS tests to exercise this scenario.
+ if (sVerbose) {
+ Slog.v(TAG, "keeping session " + id + " when server returned null but "
+ + "there is an AugmentedAutofill for user");
+ }
+ }
+ }
+
+ /**
+ * Tries to trigger Augmented Autofill when the standard service could not fulfill a request.
+ *
+ * @return callback to the Augmented Autofill service, or {@code null} if not supported.
+ */
+ // TODO(b/111330312): might need to call it in other places, like when the service returns a
+ // non-null response but without datasets (for example, just SaveInfo)
+ @GuardedBy("mLock")
+ private AugmentedAutofillCallback triggerAugmentedAutofillLocked() {
+ // Check if Smart Suggestions is supported...
+ final @SmartSuggestionMode int supportedModes = mService
+ .getSupportedSmartSuggestionModesLocked();
+ if (supportedModes == 0) return null;
+
+ // ...then if the service is set for the user
+ final IntelligenceManagerInternal intelligenceManagerInternal = mService
+ .getMaster().mIntelligenceManagerInternal;
+ if (intelligenceManagerInternal == null) return null;
+
+ // Define which mode will be used
+ final int mode;
+ if ((supportedModes & FLAG_SMART_SUGGESTION_IME) != 0) {
+ // TODO(b/111330312): support it :-)
+ Slog.w(TAG, "Smart Suggestions on IME not supported yet");
+ return null;
+ } else if ((supportedModes & FLAG_SMART_SUGGESTION_SYSTEM) != 0) {
+ mode = FLAG_SMART_SUGGESTION_SYSTEM;
+ } else {
+ Slog.w(TAG, "Unsupported Smart Suggestion Mode: " + supportedModes);
+ return null;
+ }
+
+ if (mCurrentViewId == null) {
+ Slog.w(TAG, "triggerAugmentedAutofillLocked(): no view currently focused");
+ return null;
+ }
+
+ if (sVerbose) {
+ Slog.v(TAG, "calling IntelligenseService on view " + mCurrentViewId
+ + " using suggestion mode " + smartSuggestionFlagsToString(mode)
+ + " when server returned null for session " + this.id);
+ }
+
+ // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize
+ // furgher AFM -> AFMS calls.
+ // TODO(b/119638958): add CTS tests
+ return intelligenceManagerInternal.requestAutofill(mService.getUserId(), mClient,
+ mActivityToken, this.id, mCurrentViewId);
}
@GuardedBy("mLock")
@@ -2786,6 +2864,9 @@
pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
mSaveOnAllViewsInvisible);
pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
+ if (mAugmentedAutofillCallback != null) {
+ pw.print(prefix); pw.println("has AugmentedAutofillCallback");
+ }
mRemoteFillService.dump(prefix, pw);
}
@@ -2957,6 +3038,14 @@
Slog.e(TAG, "Error notifying client to finish session", e);
}
}
+ destroyAugmentedAutofillWindowsLocked();
+ }
+
+ @GuardedBy("mLock")
+ void destroyAugmentedAutofillWindowsLocked() {
+ if (mAugmentedAutofillCallback != null) {
+ mAugmentedAutofillCallback.destroy();
+ }
}
/**
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 7621ada..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,2091 +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;
- }
- } 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();
}
/**
@@ -2915,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.
*/
@@ -2936,9 +302,8 @@
@Nullable Intent configurationIntent,
String currentDestinationString,
@Nullable Intent dataManagementIntent,
- @Nullable String dataManagementLabel) {
- updateTransportAttributes(
- Binder.getCallingUid(),
+ String dataManagementLabel) {
+ mUserBackupManagerService.updateTransportAttributes(
transportComponent,
name,
configurationIntent,
@@ -2947,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);
}
/**
@@ -3022,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);
}
/**
@@ -3094,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);
}
/**
@@ -3118,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);
}
/**
@@ -3155,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) {
@@ -3619,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 c9f7218..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;
@@ -164,24 +164,21 @@
}
/**
- * Don't write apks for forward-locked apps or system-bundled apps that are not upgraded.
+ * Don't write apks for system-bundled apps that are not upgraded.
*/
private boolean shouldWriteApk(
ApplicationInfo applicationInfo, boolean includeApks, boolean isSharedStorage) {
- boolean isForwardLocked =
- (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
boolean isSystemApp = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
boolean isUpdatedSystemApp =
(applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
return includeApks
&& !isSharedStorage
- && !isForwardLocked
&& (!isSystemApp || isUpdatedSystemApp);
}
}
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/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
index b37888f..71d261c 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -163,6 +163,20 @@
}
/**
+ * Gets the user associated with this service.
+ */
+ public final @UserIdInt int getUserId() {
+ return mUserId;
+ }
+
+ /**
+ * Gets the master service.
+ */
+ public final M getMaster() {
+ return mMaster;
+ }
+
+ /**
* Gets this UID of the remote service this service binds to, or {@code -1} if the service is
* disabled.
*/
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java
index 181d7fd..0d4cf6b 100644
--- a/services/core/java/com/android/server/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/AbstractRemoteService.java
@@ -205,6 +205,9 @@
protected void scheduleUnbind() {
cancelScheduledUnbind();
+ // TODO(b/111276913): implement "permanent binding"
+ // TODO(b/117779333): make sure it's unbound if the service settings changing (right now
+ // it's not)
mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this)
.setWhat(MSG_UNBIND), getTimeoutIdleBindMillis());
}
@@ -250,7 +253,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);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 74c8023..bb3b9f7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -894,10 +894,18 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(
- mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
+ // Listen to package add and removal events for all users.
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ mContext.registerReceiverAsUser(
+ mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+
try {
mNMS.registerObserver(mTethering);
mNMS.registerObserver(mDataActivityObserver);
@@ -1659,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
@@ -2073,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(",");
@@ -4155,6 +4176,7 @@
}
private void onUserAdded(int userId) {
+ mPermissionMonitor.onUserAdded(userId);
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
@@ -4165,6 +4187,7 @@
}
private void onUserRemoved(int userId) {
+ mPermissionMonitor.onUserRemoved(userId);
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
@@ -4174,6 +4197,22 @@
}
}
+ private void onPackageAdded(String packageName, int uid) {
+ if (TextUtils.isEmpty(packageName) || uid < 0) {
+ Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid);
+ return;
+ }
+ mPermissionMonitor.onPackageAdded(packageName, uid);
+ }
+
+ private void onPackageRemoved(String packageName, int uid) {
+ if (TextUtils.isEmpty(packageName) || uid < 0) {
+ Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
+ return;
+ }
+ mPermissionMonitor.onPackageRemoved(uid);
+ }
+
private void onUserUnlocked(int userId) {
synchronized (mVpns) {
// User present may be sent because of an unlock, which might mean an unlocked keystore.
@@ -4185,11 +4224,15 @@
}
}
- private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final Uri packageData = intent.getData();
+ final String packageName =
+ packageData != null ? packageData.getSchemeSpecificPart() : null;
if (userId == UserHandle.USER_NULL) return;
if (Intent.ACTION_USER_STARTED.equals(action)) {
@@ -4202,6 +4245,10 @@
onUserRemoved(userId);
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
onUserUnlocked(userId);
+ } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ onPackageAdded(packageName, uid);
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ onPackageRemoved(packageName, uid);
}
}
};
@@ -4728,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
@@ -4744,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);
@@ -4754,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 {
@@ -4766,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);
@@ -5072,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) {
@@ -5239,7 +5284,7 @@
notifyLockdownVpn(newNetwork);
handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
- updateTcpBufferSizes(newNetwork);
+ updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
notifyIfacesChangedForNetworkStats();
}
@@ -5654,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 8c25917..7ee3d3b 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -24,6 +24,7 @@
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
@@ -62,6 +63,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import libcore.io.IoUtils;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -73,8 +76,6 @@
import java.util.ArrayList;
import java.util.List;
-import libcore.io.IoUtils;
-
/**
* A service to manage multiple clients that want to access the IpSec API. The service is
* responsible for maintaining a list of clients and managing the resources (and related quotas)
@@ -621,7 +622,8 @@
mConfig.getDestinationAddress(),
spi,
mConfig.getMarkValue(),
- mConfig.getMarkMask());
+ mConfig.getMarkMask(),
+ mConfig.getXfrmInterfaceId());
} catch (RemoteException | ServiceSpecificException e) {
Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
}
@@ -683,7 +685,8 @@
mSrvConfig
.getNetdInstance()
.ipSecDeleteSecurityAssociation(
- uid, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
+ uid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
+ 0 /* mask */, 0 /* if_id */);
}
} catch (ServiceSpecificException | RemoteException e) {
Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
@@ -795,6 +798,8 @@
private final int mIkey;
private final int mOkey;
+ private final int mIfId;
+
TunnelInterfaceRecord(
int resourceId,
String interfaceName,
@@ -802,7 +807,8 @@
String localAddr,
String remoteAddr,
int ikey,
- int okey) {
+ int okey,
+ int intfId) {
super(resourceId);
mInterfaceName = interfaceName;
@@ -811,6 +817,7 @@
mRemoteAddress = remoteAddr;
mIkey = ikey;
mOkey = okey;
+ mIfId = intfId;
}
/** always guarded by IpSecService#this */
@@ -821,7 +828,7 @@
// Delete global policies
try {
final INetd netd = mSrvConfig.getNetdInstance();
- netd.removeVirtualTunnelInterface(mInterfaceName);
+ netd.ipSecRemoveTunnelInterface(mInterfaceName);
for (int selAddrFamily : ADDRESS_FAMILIES) {
netd.ipSecDeleteSecurityPolicy(
@@ -829,13 +836,15 @@
selAddrFamily,
IpSecManager.DIRECTION_OUT,
mOkey,
- 0xffffffff);
+ 0xffffffff,
+ mIfId);
netd.ipSecDeleteSecurityPolicy(
uid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
mIkey,
- 0xffffffff);
+ 0xffffffff,
+ mIfId);
}
} catch (ServiceSpecificException | RemoteException e) {
Log.e(
@@ -877,6 +886,10 @@
return mOkey;
}
+ public int getIfId() {
+ return mIfId;
+ }
+
@Override
protected ResourceTracker getResourceTracker() {
return getUserRecord().mTunnelQuotaTracker;
@@ -1286,7 +1299,7 @@
// Add inbound/outbound global policies
// (use reqid = 0)
final INetd netd = mSrvConfig.getNetdInstance();
- netd.addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
+ netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
for (int selAddrFamily : ADDRESS_FAMILIES) {
// Always send down correct local/remote addresses for template.
@@ -1298,7 +1311,8 @@
remoteAddr,
0,
okey,
- 0xffffffff);
+ 0xffffffff,
+ resourceId);
netd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
@@ -1307,7 +1321,8 @@
localAddr,
0,
ikey,
- 0xffffffff);
+ 0xffffffff,
+ resourceId);
}
userRecord.mTunnelInterfaceRecords.put(
@@ -1320,7 +1335,8 @@
localAddr,
remoteAddr,
ikey,
- okey),
+ okey,
+ resourceId),
binder));
return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
} catch (RemoteException e) {
@@ -1523,6 +1539,9 @@
throw new IllegalArgumentException(
"Invalid IpSecTransform.mode: " + config.getMode());
}
+
+ config.setMarkValue(0);
+ config.setMarkMask(0);
}
private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
@@ -1584,7 +1603,8 @@
(authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
encapType,
encapLocalPort,
- encapRemotePort);
+ encapRemotePort,
+ c.getXfrmInterfaceId());
}
/**
@@ -1740,27 +1760,48 @@
: tunnelInterfaceInfo.getIkey();
try {
- c.setMarkValue(mark);
- c.setMarkMask(0xffffffff);
+ // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
+ // SPI matching as part of the template resolution.
+ int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
+ c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
+
+ // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
+ // (and backporting) would allow us to narrow the mark space, and ensure that the SA
+ // and SPs have matching marks (as VTI are meant to be built).
+ // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
+ // config matches the actual allocated resources in the kernel.
+ // All SAs will have zero marks (from creation time), and any policy that matches the
+ // same src/dst could match these SAs. Non-IpSecService governed processes that
+ // establish floating policies with the same src/dst may result in undefined
+ // behavior. This is generally limited to vendor code due to the permissions
+ // (CAP_NET_ADMIN) required.
+ //
+ // c.setMarkValue(mark);
+ // c.setMarkMask(0xffffffff);
if (direction == IpSecManager.DIRECTION_OUT) {
// Set output mark via underlying network (output only)
c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
- // If outbound, also add SPI to the policy.
- for (int selAddrFamily : ADDRESS_FAMILIES) {
- mSrvConfig
- .getNetdInstance()
- .ipSecUpdateSecurityPolicy(
- callingUid,
- selAddrFamily,
- direction,
- tunnelInterfaceInfo.getLocalAddress(),
- tunnelInterfaceInfo.getRemoteAddress(),
- transformInfo.getSpiRecord().getSpi(),
- mark,
- 0xffffffff);
- }
+ // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
+ // but want to guarantee outbound packets are sent over the new SA.
+ spi = transformInfo.getSpiRecord().getSpi();
+ }
+
+ // Always update the policy with the relevant XFRM_IF_ID
+ for (int selAddrFamily : ADDRESS_FAMILIES) {
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecUpdateSecurityPolicy(
+ callingUid,
+ selAddrFamily,
+ direction,
+ transformInfo.getConfig().getSourceAddress(),
+ transformInfo.getConfig().getDestinationAddress(),
+ spi, // If outbound, also add SPI to the policy.
+ mark, // Must always set policy mark; ikey/okey for VTIs
+ 0xffffffff,
+ c.getXfrmInterfaceId());
}
// Update SA with tunnel mark (ikey or okey based on direction)
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 046442a..e5275e5 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,6 +17,7 @@
package com.android.server;
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;
@@ -2990,7 +2991,7 @@
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> deadUpdateRecords = null;
- // Broadcast location or status to all listeners
+ // Broadcast location to all listeners
for (UpdateRecord r : records) {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
@@ -3049,14 +3050,19 @@
}
}
- long prevStatusUpdateTime = r.mLastStatusBroadcast;
- if ((newStatusUpdateTime > prevStatusUpdateTime) &&
- (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
+ // TODO: location provider status callbacks have been disabled and deprecated, and are
+ // guarded behind this setting now. should be removed completely post-Q
+ if (Settings.Global.getInt(mContext.getContentResolver(),
+ LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
+ long prevStatusUpdateTime = r.mLastStatusBroadcast;
+ if ((newStatusUpdateTime > prevStatusUpdateTime)
+ && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
- r.mLastStatusBroadcast = newStatusUpdateTime;
- if (!receiver.callStatusChangedLocked(provider, status, extras)) {
- receiverDead = true;
- Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
+ r.mLastStatusBroadcast = newStatusUpdateTime;
+ if (!receiver.callStatusChangedLocked(provider, status, extras)) {
+ receiverDead = true;
+ Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
+ }
}
}
@@ -3276,7 +3282,6 @@
// we don't leave anything dangling.
clearTestProviderEnabled(provider, opPackageName);
clearTestProviderLocation(provider, opPackageName);
- clearTestProviderStatus(provider, opPackageName);
MockProvider mockProvider = mMockProviders.remove(provider);
if (mockProvider == null) {
@@ -3409,21 +3414,6 @@
}
@Override
- public void clearTestProviderStatus(String provider, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
- return;
- }
-
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- mockProvider.clearStatus();
- }
- }
-
- @Override
public PendingIntent createManageLocationPermissionIntent(String packageName,
String permission) {
Preconditions.checkNotNull(packageName);
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index 2dee3a0..c563ad2 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -141,6 +141,7 @@
if (mEnabled != enabled) {
mEnabled = enabled;
mStats.reset();
+ mStats.setAddDebugEntries(enabled);
Looper.setObserver(enabled ? mStats : null);
}
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 92d8d73..87a42fa 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1328,40 +1328,12 @@
modifyInterfaceForward(false, fromIface, toIface);
}
- private void modifyNat(String action, String internalInterface, String externalInterface)
- throws SocketException {
- final Command cmd = new Command("nat", action, internalInterface, externalInterface);
-
- final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
- internalInterface);
- if (internalNetworkInterface == null) {
- cmd.appendArg("0");
- } else {
- // Don't touch link-local routes, as link-local addresses aren't routable,
- // kernel creates link-local routes on all interfaces automatically
- List<InterfaceAddress> interfaceAddresses = excludeLinkLocal(
- internalNetworkInterface.getInterfaceAddresses());
- cmd.appendArg(interfaceAddresses.size());
- for (InterfaceAddress ia : interfaceAddresses) {
- InetAddress addr = NetworkUtils.getNetworkPart(
- ia.getAddress(), ia.getNetworkPrefixLength());
- cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
- }
- }
-
- try {
- mConnector.execute(cmd);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
@Override
public void enableNat(String internalInterface, String externalInterface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- modifyNat("enable", internalInterface, externalInterface);
- } catch (SocketException e) {
+ mNetdService.tetherAddForward(internalInterface, externalInterface);
+ } catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
}
@@ -1370,8 +1342,8 @@
public void disableNat(String internalInterface, String externalInterface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- modifyNat("disable", internalInterface, externalInterface);
- } catch (SocketException e) {
+ mNetdService.tetherRemoveForward(internalInterface, externalInterface);
+ } catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c660cc6..a19e928 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1702,8 +1702,11 @@
s.app.whitelistManager = true;
}
// This could have made the service more important.
- mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities()
- || s.app.treatLikeActivity, b.client);
+ mAm.updateLruProcessLocked(s.app,
+ (callerApp.hasActivitiesOrRecentTasks() && s.app.hasClientActivities())
+ || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
+ && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
+ b.client);
mAm.updateOomAdjLocked(s.app, true);
}
@@ -1787,6 +1790,32 @@
}
}
+ void updateServiceGroupLocked(IServiceConnection connection, int group, int importance) {
+ final IBinder binder = connection.asBinder();
+ if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "updateServiceGroup: conn=" + binder);
+ final ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+ if (clist == null) {
+ throw new IllegalArgumentException("Could not find connection for "
+ + connection.asBinder());
+ }
+ for (int i = clist.size() - 1; i >= 0; i--) {
+ final ConnectionRecord crec = clist.get(i);
+ final ServiceRecord srec = crec.binding.service;
+ if (srec != null && srec.app != null
+ && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+ if (group > 0) {
+ srec.app.connectionService = srec;
+ srec.app.connectionGroup = group;
+ srec.app.connectionImportance = importance;
+ } else {
+ srec.app.connectionService = null;
+ srec.app.connectionGroup = 0;
+ srec.app.connectionImportance = 0;
+ }
+ }
+ }
+ }
+
boolean unbindServiceLocked(IServiceConnection connection) {
IBinder binder = connection.asBinder();
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "unbindService: conn=" + binder);
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 4e417ba..b62f648 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -497,6 +497,18 @@
private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB
/**
+ * The number of binder proxies we need to have before we start warning and
+ * dumping debug info.
+ */
+ private static final int BINDER_PROXY_HIGH_WATERMARK = 6000;
+
+ /**
+ * Low watermark that needs to be met before we consider dumping info again,
+ * after already hitting the high watermark.
+ */
+ private static final int BINDER_PROXY_LOW_WATERMARK = 5500;
+
+ /**
* State indicating that there is no need for any blocking for network.
*/
@VisibleForTesting
@@ -4360,7 +4372,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;
@@ -4567,6 +4578,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");
@@ -8477,7 +8492,8 @@
mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
- BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
+ BinderInternal.nSetBinderProxyCountWatermarks(BINDER_PROXY_HIGH_WATERMARK,
+ BINDER_PROXY_LOW_WATERMARK);
BinderInternal.nSetBinderProxyCountEnabled(true);
BinderInternal.setBinderProxyCountCallback(
new BinderInternal.BinderProxyLimitListener() {
@@ -9207,26 +9223,41 @@
}
dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
}
+ if (dumpPackage == null) {
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ mOomAdjProfiler.dump(pw);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpLmkLocked(pw);
+ }
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpLruLocked(pw, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
+ }
+ if (dumpPackage == null) {
+ // Intentionally dropping the lock for this, because dumpBinderProxies() will make many
+ // outgoing binder calls to retrieve interface descriptors; while that is system code,
+ // there is nothing preventing an app from overriding this implementation by talking to
+ // the binder driver directly, and hang up system_server in the process. So, dump
+ // without locks held, and even then only when there is an unreasonably large number of
+ // proxies in the first place.
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- mOomAdjProfiler.dump(pw);
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
- }
- dumpBinderProxies(pw);
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
- }
- dumpLmkLocked(pw);
+ dumpBinderProxies(pw, BINDER_PROXY_HIGH_WATERMARK /* minToDump */);
}
}
@@ -9366,7 +9397,7 @@
cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage);
} else if ("binder-proxies".equals(cmd)) {
if (opti >= args.length) {
- dumpBinderProxies(pw);
+ dumpBinderProxies(pw, 0 /* minToDump */);
} else {
String uid = args[opti];
opti++;
@@ -9421,6 +9452,10 @@
synchronized (this) {
dumpLmkLocked(pw);
}
+ } else if ("lru".equals(cmd)) {
+ synchronized (this) {
+ dumpLruLocked(pw, null);
+ }
} else if ("permissions".equals(cmd) || "perm".equals(cmd)) {
synchronized (this) {
dumpPermissionsLocked(fd, pw, args, opti, true, null);
@@ -9698,17 +9733,109 @@
}
pw.println();
}
- pw.println();
return true;
}
return false;
}
- void dumpBinderProxies(PrintWriter pw) {
- dumpBinderProxyInterfaceCounts(pw,
- "Top proxy interface names held by SYSTEM");
+ void dumpBinderProxies(PrintWriter pw, int minCountToDumpInterfaces) {
+ pw.println("ACTIVITY MANAGER BINDER PROXY STATE (dumpsys activity binder-proxies)");
+ final int proxyCount = BinderProxy.getProxyCount();
+ if (proxyCount >= minCountToDumpInterfaces) {
+ dumpBinderProxyInterfaceCounts(pw,
+ "Top proxy interface names held by SYSTEM");
+ } else {
+ pw.print("Not dumping proxy interface counts because size ("
+ + Integer.toString(proxyCount) + ") looks reasonable");
+ pw.println();
+ }
dumpBinderProxiesCounts(pw,
- "Counts of Binder Proxies held by SYSTEM");
+ " Counts of Binder Proxies held by SYSTEM");
+ }
+
+ void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc) {
+ pw.print(" #");
+ pw.print(index);
+ pw.print(": ");
+ pw.print(ProcessList.makeOomAdjString(proc.setAdj));
+ pw.print(" ");
+ pw.print(ProcessList.makeProcStateString(proc.getCurProcState()));
+ pw.print(" ");
+ pw.print(proc.toShortString());
+ pw.print(" ");
+ if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities()
+ || proc.treatLikeActivity) {
+ pw.print(" activity=");
+ boolean printed = false;
+ if (proc.hasActivities()) {
+ pw.print("activities");
+ printed = true;
+ }
+ if (proc.hasRecentTasks()) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("recents");
+ printed = true;
+ }
+ if (proc.hasClientActivities()) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("client");
+ printed = true;
+ }
+ if (proc.treatLikeActivity) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("treated");
+ }
+ }
+ pw.println();
+ }
+
+ // TODO: Move to ProcessList?
+ void dumpLruLocked(PrintWriter pw, String dumpPackage) {
+ pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity lru)");
+ final int N = mProcessList.mLruProcesses.size();
+ int i;
+ boolean first = true;
+ for (i = N - 1; i >= mProcessList.mLruProcessActivityStart; i--) {
+ final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.println(" Activities:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r);
+ }
+ first = true;
+ for (; i >= mProcessList.mLruProcessServiceStart; i--) {
+ final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.println(" Services:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r);
+ }
+ first = true;
+ for (; i >= 0; i--) {
+ final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.println(" Other:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r);
+ }
}
// TODO: Move to ProcessList?
@@ -13214,6 +13341,12 @@
}
}
+ public void updateServiceGroup(IServiceConnection connection, int group, int importance) {
+ synchronized (this) {
+ mServices.updateServiceGroupLocked(connection, group, importance);
+ }
+ }
+
public boolean unbindService(IServiceConnection connection) {
synchronized (this) {
return mServices.unbindServiceLocked(connection);
@@ -17398,8 +17531,11 @@
int stepCached = 0;
int stepEmpty = 0;
int numCached = 0;
+ int numCachedExtraGroup = 0;
int numEmpty = 0;
int numTrimming = 0;
+ int lastCachedGroup = 0;
+ int lastCachedGroupUid = 0;
mNumNonCachedProcs = 0;
mNumCachedHiddenProcs = 0;
@@ -17523,7 +17659,21 @@
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
mNumCachedHiddenProcs++;
numCached++;
- if (numCached > cachedProcessLimit) {
+ if (app.connectionGroup != 0) {
+ if (lastCachedGroupUid == app.uid
+ && lastCachedGroup == app.connectionGroup) {
+ // If this process is the next in the same group, we don't
+ // want it to count against our limit of the number of cached
+ // processes, so bump up the group count to account for it.
+ numCachedExtraGroup++;
+ } else {
+ lastCachedGroupUid = app.uid;
+ lastCachedGroup = app.connectionGroup;
+ }
+ } else {
+ lastCachedGroupUid = lastCachedGroup = 0;
+ }
+ if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
app.kill("cached #" + numCached, true);
}
break;
@@ -19054,6 +19204,10 @@
return mConstants.mFlagActivityStartsLoggingEnabled;
}
+ public boolean isBackgroundActivityStartsEnabled() {
+ return mConstants.mFlagBackgroundActivityStartsEnabled;
+ }
+
public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
synchronized(ActivityManagerService.this) {
ActivityManagerService.this.reportGlobalUsageEventLocked(keyguardShowing
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 8f8d5ab..67a4d14 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2853,6 +2853,9 @@
pw.println(" provider [COMP_SPEC]: provider client-side state");
pw.println(" s[ervices] [COMP_SPEC ...]: service state");
pw.println(" as[sociations]: tracked app associations");
+ pw.println(" lmk: stats on low memory killer");
+ pw.println(" lru: raw LRU process list");
+ pw.println(" binder-proxies: stats on binder objects and IPCs");
pw.println(" settings: currently applied config settings");
pw.println(" service [COMP_SPEC]: service client-side state");
pw.println(" package [PACKAGE_NAME]: all state related to given package");
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 4fe7d03..09df7e20 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -222,11 +222,11 @@
receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities);
boolean result = requestAutofillData
- ? ActivityTaskManager.getService().requestAssistContextExtras(
+ ? ActivityTaskManager.getService().requestAutofillData(this,
+ receiverExtras, topActivity, 0 /* flags */)
+ : ActivityTaskManager.getService().requestAssistContextExtras(
ASSIST_CONTEXT_FULL, this, receiverExtras, topActivity,
- /* focused= */ i == 0, /* newSessionId= */ i == 0)
- : ActivityTaskManager.getService().requestAutofillData(this,
- receiverExtras, topActivity, 0 /* flags */);
+ /* focused= */ i == 0, /* newSessionId= */ i == 0);
if (result) {
mPendingDataCount++;
} else if (i == 0) {
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index bfa3f66..aa76b3d 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -48,7 +48,7 @@
boolean serviceDead; // Well is it?
// Please keep the following two enum list synced.
- private static int[] BIND_ORIG_ENUMS = new int[] {
+ private static final int[] BIND_ORIG_ENUMS = new int[] {
Context.BIND_AUTO_CREATE,
Context.BIND_DEBUG_UNBIND,
Context.BIND_NOT_FOREGROUND,
@@ -65,7 +65,7 @@
Context.BIND_SHOWING_UI,
Context.BIND_NOT_VISIBLE,
};
- private static int[] BIND_PROTO_ENUMS = new int[] {
+ private static final int[] BIND_PROTO_ENUMS = new int[] {
ConnectionRecordProto.AUTO_CREATE,
ConnectionRecordProto.DEBUG_UNBIND,
ConnectionRecordProto.NOT_FG,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 84b364b..4b19398 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -50,6 +50,7 @@
import android.app.AppProtoEnums;
import android.app.IApplicationThread;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -2204,7 +2205,7 @@
@GuardedBy("mService")
int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
- String what, Object obj, ProcessRecord srcApp) {
+ int lruSeq, String what, Object obj, ProcessRecord srcApp) {
app.lastActivityTime = now;
if (app.hasActivitiesOrRecentTasks()) {
@@ -2225,7 +2226,7 @@
return index;
}
- if (lrui >= mLruProcessActivityStart) {
+ if (lrui >= mLruProcessActivityStart && index < mLruProcessActivityStart) {
// Don't want to touch dependent processes that are hosting activities.
return index;
}
@@ -2237,6 +2238,7 @@
if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+ " in LRU list: " + app);
mLruProcesses.add(index, app);
+ app.lruSeq = lruSeq;
return index;
}
@@ -2345,9 +2347,11 @@
*/
int nextIndex;
+ int nextActivityIndex = -1;
if (hasActivity) {
final int N = mLruProcesses.size();
- if ((!app.hasActivities() || app.hasRecentTasks())
+ nextIndex = mLruProcessServiceStart;
+ if (!app.hasActivitiesOrRecentTasks() && !app.treatLikeActivity
&& mLruProcessActivityStart < (N - 1)) {
// Process doesn't have activities, but has clients with
// activities... move it up, but one below the top (the top
@@ -2355,36 +2359,92 @@
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Adding to second-top of LRU activity list: " + app);
mLruProcesses.add(N - 1, app);
- // To keep it from spamming the LRU list (by making a bunch of clients),
- // we will push down any other entries owned by the app.
+ // If this process is part of a group, need to pull up any other processes
+ // in that group to be with it.
final int uid = app.info.uid;
- for (int i = N - 2; i > mLruProcessActivityStart; i--) {
- ProcessRecord subProc = mLruProcesses.get(i);
- if (subProc.info.uid == uid) {
- // We want to push this one down the list. If the process after
- // it is for the same uid, however, don't do so, because we don't
- // want them internally to be re-ordered.
- if (mLruProcesses.get(i - 1).info.uid != uid) {
- if (DEBUG_LRU) Slog.d(TAG_LRU,
- "Pushing uid " + uid + " swapping at " + i + ": "
- + mLruProcesses.get(i) + " : "
- + mLruProcesses.get(i - 1));
- ProcessRecord tmp = mLruProcesses.get(i);
- mLruProcesses.set(i, mLruProcesses.get(i - 1));
- mLruProcesses.set(i - 1, tmp);
- i--;
+ int endIndex = N - 2;
+ nextActivityIndex = N - 2;
+ if (app.connectionGroup > 0) {
+ int endImportance = app.connectionImportance;
+ for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
+ final ProcessRecord subProc = mLruProcesses.get(i);
+ if (subProc.info.uid == uid
+ && subProc.connectionGroup == subProc.connectionGroup) {
+ if (i == endIndex && subProc.connectionImportance >= endImportance) {
+ // This process is already in the group, and its importance
+ // is not as strong as the process before it, so it keep it
+ // correctly positioned in the group.
+ endIndex--;
+ endImportance = subProc.connectionImportance;
+ } else {
+ // We want to pull this up to be with the rest of the group,
+ // and order within the group by importance.
+ boolean moved = false;
+ for (int pos = N - 1; pos > endIndex; pos--) {
+ final ProcessRecord posProc = mLruProcesses.get(pos);
+ if (subProc.connectionImportance
+ <= posProc.connectionImportance) {
+ mLruProcesses.remove(i);
+ mLruProcesses.add(pos, subProc);
+ moved = true;
+ endIndex--;
+ break;
+ }
+ }
+ if (!moved) {
+ // Goes to the end of the group.
+ mLruProcesses.remove(i);
+ mLruProcesses.add(endIndex - 1, subProc);
+ endIndex--;
+ endImportance = subProc.connectionImportance;
+ }
+ }
}
- } else {
- // A gap, we can stop here.
- break;
+ }
+
+ }
+ // To keep it from spamming the LRU list (by making a bunch of clients),
+ // we will distribute other entries owned by it to be in-between other apps.
+ for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
+ final ProcessRecord subProc = mLruProcesses.get(i);
+ if (subProc.info.uid != uid) {
+ // This is a different app... if we have gone through some of the
+ // target app, pull this up to be before them.
+ if (i < endIndex) {
+ mLruProcesses.remove(i);
+ mLruProcesses.add(endIndex, subProc);
+ }
+ // Find the end of the next group of processes for target app. This
+ // is after any entries of different apps (so we don't change the existing
+ // relative order of apps) and then after the next last group of processes
+ // of the target app.
+ for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
+ final ProcessRecord endProc = mLruProcesses.get(endIndex);
+ if (endProc.info.uid == uid) {
+ break;
+ }
+ }
+ if (endIndex >= mLruProcessActivityStart) {
+ final ProcessRecord endProc = mLruProcesses.get(endIndex);
+ for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
+ final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
+ if (nextEndProc.info.uid != uid
+ || nextEndProc.connectionGroup != endProc.connectionGroup) {
+ break;
+ }
+ }
+ }
+ if (i > endIndex) {
+ i = endIndex;
+ }
}
}
} else {
// Process has activities, put it at the very tipsy-top.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
mLruProcesses.add(app);
+ nextActivityIndex = mLruProcesses.size() - 1;
}
- nextIndex = mLruProcessServiceStart;
} else if (hasService) {
// Process has services, put it at the top of the service list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
@@ -2416,6 +2476,8 @@
mLruProcessServiceStart++;
}
+ app.lruSeq = mLruSeq;
+
// If the app is currently using a content provider or service,
// bump those processes as well.
for (int j = app.connections.size() - 1; j >= 0; j--) {
@@ -2423,17 +2485,27 @@
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq
+ && (cr.flags & Context.BIND_REDUCTION_FLAGS) == 0
&& !cr.binding.service.app.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
- now,
- nextIndex,
- "service connection", cr, app);
+ if (cr.binding.service.app.hasClientActivities()) {
+ if (nextActivityIndex >= 0) {
+ nextActivityIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+ now,
+ nextActivityIndex, mLruSeq,
+ "service connection", cr, app);
+ }
+ } else {
+ nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+ now,
+ nextIndex, mLruSeq,
+ "service connection", cr, app);
+ }
}
}
for (int j = app.conProviders.size() - 1; j >= 0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
+ nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, mLruSeq,
"provider reference", cpr, app);
}
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index faf8561..013de93 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -155,6 +155,9 @@
int pssStatType; // The type of stat collection that we are currently requesting
int savedPriority; // Previous priority value if we're switching to non-SCHED_OTHER
int renderThreadTid; // TID for RenderThread
+ ServiceRecord connectionService; // Service that applied current connectionGroup/Importance
+ int connectionGroup; // Last group set by a connection
+ int connectionImportance; // Last importance set by a connection
boolean serviceb; // Process currently is on the service B list
boolean serviceHighRam; // We are forcing to service B list due to its RAM use
boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
@@ -396,6 +399,11 @@
pw.print(" hasAboveClient="); pw.print(hasAboveClient);
pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
}
+ if (connectionService != null || connectionGroup != 0) {
+ pw.print(prefix); pw.print("connectionGroup="); pw.print(connectionGroup);
+ pw.print(" Importance="); pw.print(connectionImportance);
+ pw.print(" Service="); pw.println(connectionService);
+ }
if (hasTopUi() || hasOverlayUi() || runningRemoteAnimation) {
pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
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/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index c3e3842..c2f4406 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;
@@ -314,6 +330,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) {
@@ -359,6 +381,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 +535,9 @@
sendMessage(CMD_EVALUATE_PRIVATE_DNS);
break;
}
+ case EVENT_DNS_NOTIFICATION:
+ mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
+ break;
default:
break;
}
@@ -537,6 +568,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;
}
@@ -856,6 +894,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).
@@ -883,6 +922,7 @@
// Leave the event to EvaluatingState. Defer this message will result in reset
// of mReevaluateDelayMs and mEvaluateAttempts.
case CMD_NETWORK_DISCONNECTED:
+ case EVENT_DNS_NOTIFICATION:
return NOT_HANDLED;
default:
// TODO: Some events may able to handle in this state, instead of deferring to
@@ -947,6 +987,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 +1525,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/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 94c94a5..420b23e 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -27,10 +27,7 @@
import static android.os.Process.SYSTEM_UID;
import android.annotation.NonNull;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -42,7 +39,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -64,15 +60,14 @@
public class PermissionMonitor {
private static final String TAG = "PermissionMonitor";
private static final boolean DBG = true;
- private static final Boolean SYSTEM = Boolean.TRUE;
- private static final Boolean NETWORK = Boolean.FALSE;
+ protected static final Boolean SYSTEM = Boolean.TRUE;
+ protected static final Boolean NETWORK = Boolean.FALSE;
private static final int VERSION_Q = Build.VERSION_CODES.Q;
private final Context mContext;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final INetworkManagementService mNetd;
- private final BroadcastReceiver mIntentReceiver;
// Values are User IDs.
private final Set<Integer> mUsers = new HashSet<>();
@@ -85,26 +80,6 @@
mPackageManager = context.getPackageManager();
mUserManager = UserManager.get(context);
mNetd = netd;
- mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
- Uri appData = intent.getData();
- String appName = appData != null ? appData.getSchemeSpecificPart() : null;
-
- if (Intent.ACTION_USER_ADDED.equals(action)) {
- onUserAdded(user);
- } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- onUserRemoved(user);
- } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
- onAppAdded(appName, appUid);
- } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- onAppRemoved(appUid);
- }
- }
- };
}
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -112,17 +87,6 @@
public synchronized void startMonitoring() {
log("Monitoring");
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_ADDED);
- intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addDataScheme("package");
- mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
if (apps == null) {
loge("No apps");
@@ -260,7 +224,14 @@
}
}
- private synchronized void onUserAdded(int user) {
+ /**
+ * Called when a user is added. See {link #ACTION_USER_ADDED}.
+ *
+ * @param user The integer userHandle of the added user. See {@link #EXTRA_USER_HANDLE}.
+ *
+ * @hide
+ */
+ public synchronized void onUserAdded(int user) {
if (user < 0) {
loge("Invalid user in onUserAdded: " + user);
return;
@@ -272,7 +243,14 @@
update(users, mApps, true);
}
- private synchronized void onUserRemoved(int user) {
+ /**
+ * Called when an user is removed. See {link #ACTION_USER_REMOVED}.
+ *
+ * @param user The integer userHandle of the removed user. See {@link #EXTRA_USER_HANDLE}.
+ *
+ * @hide
+ */
+ public synchronized void onUserRemoved(int user) {
if (user < 0) {
loge("Invalid user in onUserRemoved: " + user);
return;
@@ -284,8 +262,8 @@
update(users, mApps, false);
}
-
- private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
+ @VisibleForTesting
+ protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
if (currentPermission == SYSTEM) {
return currentPermission;
}
@@ -303,33 +281,39 @@
return currentPermission;
}
- private synchronized void onAppAdded(String appName, int appUid) {
- if (TextUtils.isEmpty(appName) || appUid < 0) {
- loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
- return;
- }
-
+ /**
+ * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}.
+ *
+ * @param packageName The name of the new package.
+ * @param uid The uid of the new package.
+ *
+ * @hide
+ */
+ public synchronized void onPackageAdded(String packageName, int uid) {
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
- final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
- if (permission != mApps.get(appUid)) {
- mApps.put(appUid, permission);
+ final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
+ if (permission != mApps.get(uid)) {
+ mApps.put(uid, permission);
Map<Integer, Boolean> apps = new HashMap<>();
- apps.put(appUid, permission);
+ apps.put(uid, permission);
update(mUsers, apps, true);
}
}
- private synchronized void onAppRemoved(int appUid) {
- if (appUid < 0) {
- loge("Invalid app in onAppRemoved: " + appUid);
- return;
- }
+ /**
+ * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}.
+ *
+ * @param uid containing the integer uid previously assigned to the package.
+ *
+ * @hide
+ */
+ public synchronized void onPackageRemoved(int uid) {
Map<Integer, Boolean> apps = new HashMap<>();
Boolean permission = null;
- String[] packages = mPackageManager.getPackagesForUid(appUid);
+ String[] packages = mPackageManager.getPackagesForUid(uid);
if (packages != null && packages.length > 0) {
for (String name : packages) {
permission = highestPermissionForUid(permission, name);
@@ -341,16 +325,16 @@
}
}
}
- if (permission == mApps.get(appUid)) {
+ if (permission == mApps.get(uid)) {
// The permissions of this UID have not changed. Nothing to do.
return;
} else if (permission != null) {
- mApps.put(appUid, permission);
- apps.put(appUid, permission);
+ mApps.put(uid, permission);
+ apps.put(uid, permission);
update(mUsers, apps, true);
} else {
- mApps.remove(appUid);
- apps.put(appUid, NETWORK); // doesn't matter which permission we pick here
+ mApps.remove(uid);
+ apps.put(uid, NETWORK); // doesn't matter which permission we pick here
update(mUsers, apps, false);
}
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index a8f7259..3c14393 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -23,24 +23,18 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
-import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER;
import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
-import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
-import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
-import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
-import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
-import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_INVALID;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -50,6 +44,7 @@
import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+
import static com.android.server.ConnectivityService.SHORT_ARG;
import android.app.Notification;
@@ -60,7 +55,6 @@
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -68,7 +62,6 @@
import android.hardware.usb.UsbManager;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
-import android.net.ip.IpServer;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -76,7 +69,7 @@
import android.net.NetworkInfo;
import android.net.NetworkState;
import android.net.NetworkUtils;
-import android.net.RouteInfo;
+import android.net.ip.IpServer;
import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
@@ -89,15 +82,12 @@
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
-import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -113,6 +103,7 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.LocalServices;
+import com.android.server.connectivity.tethering.EntitlementManager;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadController;
import com.android.server.connectivity.tethering.TetheringConfiguration;
@@ -123,8 +114,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
@@ -145,18 +134,12 @@
private final static boolean DBG = false;
private final static boolean VDBG = false;
- protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
-
private static final Class[] messageClasses = {
Tethering.class, TetherMasterSM.class, IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
- // {@link ComponentName} of the Service used to run tether provisioning.
- private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
- .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
-
private static class TetherState {
public final IpServer ipServer;
public int lastState;
@@ -191,7 +174,6 @@
private final INetworkStatsService mStatsService;
private final INetworkPolicyManager mPolicyManager;
private final Looper mLooper;
- private final MockableSystemProperties mSystemProperties;
private final StateMachine mTetherMasterSM;
private final OffloadController mOffloadController;
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@@ -200,6 +182,7 @@
private final HashSet<IpServer> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
private final TetheringDependencies mDeps;
+ private final EntitlementManager mEntitlementMgr;
private volatile TetheringConfiguration mConfig;
private InterfaceSet mCurrentUpstreamIfaceSet;
@@ -220,7 +203,6 @@
mStatsService = statsService;
mPolicyManager = policyManager;
mLooper = looper;
- mSystemProperties = systemProperties;
mDeps = deps;
mPublicSync = new Object();
@@ -241,12 +223,13 @@
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
+ mEntitlementMgr = mDeps.getEntitlementManager(mContext, mLog, systemProperties);
mCarrierConfigChange = new VersionedBroadcastListener(
"CarrierConfigChangeListener", mContext, smHandler, filter,
(Intent ignored) -> {
mLog.log("OBSERVED carrier config change");
updateConfiguration();
- reevaluateSimCardProvisioning();
+ mEntitlementMgr.reevaluateSimCardProvisioning();
});
mStateReceiver = new StateReceiver();
@@ -289,6 +272,7 @@
private void updateConfiguration() {
mConfig = new TetheringConfiguration(mContext, mLog);
mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
+ mEntitlementMgr.updateConfiguration(mConfig);
}
private void maybeUpdateConfiguration() {
@@ -354,83 +338,54 @@
}
public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
- if (!isTetherProvisioningRequired()) {
+ mEntitlementMgr.startTethering(type);
+ if (!mEntitlementMgr.isTetherProvisioningRequired()) {
enableTetheringInternal(type, true, receiver);
return;
}
+ final ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
if (showProvisioningUi) {
- runUiTetherProvisioningAndEnable(type, receiver);
+ mEntitlementMgr.runUiTetherProvisioningAndEnable(type, proxyReceiver);
} else {
- runSilentTetherProvisioningAndEnable(type, receiver);
+ mEntitlementMgr.runSilentTetherProvisioningAndEnable(type, proxyReceiver);
}
}
public void stopTethering(int type) {
enableTetheringInternal(type, false, null);
- if (isTetherProvisioningRequired()) {
- cancelTetherProvisioningRechecks(type);
+ mEntitlementMgr.stopTethering(type);
+ if (mEntitlementMgr.isTetherProvisioningRequired()) {
+ // There are lurking bugs where the notion of "provisioning required" or
+ // "tethering supported" may change without notifying tethering properly, then
+ // tethering can't shutdown correctly.
+ // TODO: cancel re-check all the time
+ if (mDeps.isTetheringSupported()) {
+ mEntitlementMgr.cancelTetherProvisioningRechecks(type);
+ }
}
}
/**
- * Check if the device requires a provisioning check in order to enable tethering.
- *
- * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
- */
- @VisibleForTesting
- protected boolean isTetherProvisioningRequired() {
- final TetheringConfiguration cfg = mConfig;
- if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
- || cfg.provisioningApp.length == 0) {
- return false;
- }
- if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
- return false;
- }
- return (cfg.provisioningApp.length == 2);
- }
-
- // The logic here is aimed solely at confirming that a CarrierConfig exists
- // and affirms that entitlement checks are not required.
- //
- // TODO: find a better way to express this, or alter the checking process
- // entirely so that this is more intuitive.
- private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
- // Check carrier config for entitlement checks
- final CarrierConfigManager configManager = (CarrierConfigManager) mContext
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) return false;
-
- final PersistableBundle carrierConfig = configManager.getConfig();
- if (carrierConfig == null) return false;
-
- // A CarrierConfigManager was found and it has a config.
- final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
- CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
- return !isEntitlementCheckRequired;
- }
-
- /**
* Enables or disables tethering for the given type. This should only be called once
* provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
* for the specified interface.
*/
private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
- boolean isProvisioningRequired = enable && isTetherProvisioningRequired();
+ boolean isProvisioningRequired = enable && mEntitlementMgr.isTetherProvisioningRequired();
int result;
switch (type) {
case TETHERING_WIFI:
result = setWifiTethering(enable);
if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
- scheduleProvisioningRechecks(type);
+ mEntitlementMgr.scheduleProvisioningRechecks(type);
}
sendTetherResult(receiver, result);
break;
case TETHERING_USB:
result = setUsbTethering(enable);
if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
- scheduleProvisioningRechecks(type);
+ mEntitlementMgr.scheduleProvisioningRechecks(type);
}
sendTetherResult(receiver, result);
break;
@@ -489,32 +444,14 @@
? TETHER_ERROR_NO_ERROR
: TETHER_ERROR_MASTER_ERROR;
sendTetherResult(receiver, result);
- if (enable && isTetherProvisioningRequired()) {
- scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
+ if (enable && mEntitlementMgr.isTetherProvisioningRequired()) {
+ mEntitlementMgr.scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
}
adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
}
}, BluetoothProfile.PAN);
}
- private void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
- ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
- sendUiTetherProvisionIntent(type, proxyReceiver);
- }
-
- private void sendUiTetherProvisionIntent(int type, ResultReceiver receiver) {
- Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
/**
* Creates a proxy {@link ResultReceiver} which enables tethering if the provisioning result
* is successful before firing back up to the wrapped receiver.
@@ -546,62 +483,6 @@
return receiverForSending;
}
- private void scheduleProvisioningRechecks(int type) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_SET_ALARM, true);
- intent.setComponent(TETHER_SERVICE);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startServiceAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
- ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
- sendSilentTetherProvisionIntent(type, proxyReceiver);
- }
-
- private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_RUN_PROVISION, true);
- intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.setComponent(TETHER_SERVICE);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startServiceAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void cancelTetherProvisioningRechecks(int type) {
- if (mDeps.isTetheringSupported()) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
- intent.setComponent(TETHER_SERVICE);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startServiceAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- // Used by the SIM card change observation code.
- // TODO: De-duplicate with above code, where possible.
- private void startProvisionIntent(int tetherType) {
- final Intent startProvIntent = new Intent();
- startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
- startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
- startProvIntent.setComponent(TETHER_SERVICE);
- mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
- }
-
public int tether(String iface) {
return tether(iface, IpServer.STATE_TETHERED);
}
@@ -1166,30 +1047,6 @@
return false;
}
- private void reevaluateSimCardProvisioning() {
- if (!mConfig.hasMobileHotspotProvisionApp()) return;
- if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
-
- ArrayList<Integer> tethered = new ArrayList<>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState != IpServer.STATE_TETHERED) {
- continue; // Skip interfaces that aren't tethered.
- }
- String iface = mTetherStates.keyAt(i);
- int interfaceType = ifaceNameToType(iface);
- if (interfaceType != TETHERING_INVALID) {
- tethered.add(interfaceType);
- }
- }
- }
-
- for (int tetherType : tethered) {
- startProvisionIntent(tetherType);
- }
- }
-
class TetherMasterSM extends StateMachine {
private static final int BASE_MASTER = Protocol.BASE_TETHERING;
// an interface SM has requested Tethering/Local Hotspot
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
new file mode 100644
index 0000000..a4e3e1d
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -0,0 +1,224 @@
+/*
+ * 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.connectivity.tethering;
+
+import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
+import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
+import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
+
+import static com.android.internal.R.string.config_wifi_tether_enable;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.os.Binder;
+import android.os.PersistableBundle;
+import android.os.ResultReceiver;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.MockableSystemProperties;
+
+/**
+ * This class encapsulates entitlement/provisioning mechanics
+ * provisioning check only applies to the use of the mobile network as an upstream
+ *
+ * @hide
+ */
+public class EntitlementManager {
+ private static final String TAG = EntitlementManager.class.getSimpleName();
+
+ // {@link ComponentName} of the Service used to run tether provisioning.
+ private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
+ Resources.getSystem().getString(config_wifi_tether_enable));
+ protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
+
+ // The ArraySet contains enabled downstream types, ex:
+ // {@link ConnectivityManager.TETHERING_WIFI}
+ // {@link ConnectivityManager.TETHERING_USB}
+ // {@link ConnectivityManager.TETHERING_BLUETOOTH}
+ @GuardedBy("mCurrentTethers")
+ private final ArraySet<Integer> mCurrentTethers;
+ private final Context mContext;
+ private final MockableSystemProperties mSystemProperties;
+ private final SharedLog mLog;
+ @Nullable
+ private TetheringConfiguration mConfig;
+
+ public EntitlementManager(Context ctx, SharedLog log,
+ MockableSystemProperties systemProperties) {
+ mContext = ctx;
+ mLog = log;
+ mCurrentTethers = new ArraySet<Integer>();
+ mSystemProperties = systemProperties;
+ }
+
+ /**
+ * Pass a new TetheringConfiguration instance each time when
+ * Tethering#updateConfiguration() is called.
+ */
+ public void updateConfiguration(TetheringConfiguration conf) {
+ mConfig = conf;
+ }
+
+ /**
+ * Tell EntitlementManager that a given type of tethering has been enabled
+ *
+ * @param type Tethering type
+ */
+ public void startTethering(int type) {
+ synchronized (mCurrentTethers) {
+ mCurrentTethers.add(type);
+ }
+ }
+
+ /**
+ * Tell EntitlementManager that a given type of tethering has been disabled
+ *
+ * @param type Tethering type
+ */
+ public void stopTethering(int type) {
+ synchronized (mCurrentTethers) {
+ mCurrentTethers.remove(type);
+ }
+ }
+
+ /**
+ * Check if the device requires a provisioning check in order to enable tethering.
+ *
+ * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
+ */
+ @VisibleForTesting
+ public boolean isTetherProvisioningRequired() {
+ if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
+ || mConfig.provisioningApp.length == 0) {
+ return false;
+ }
+ if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
+ return false;
+ }
+ return (mConfig.provisioningApp.length == 2);
+ }
+
+ /**
+ * Re-check tethering provisioning for enabled downstream tether types.
+ * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
+ */
+ public void reevaluateSimCardProvisioning() {
+ if (!mConfig.hasMobileHotspotProvisionApp()) return;
+ if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
+
+ final ArraySet<Integer> reevaluateType;
+ synchronized (mCurrentTethers) {
+ reevaluateType = new ArraySet<Integer>(mCurrentTethers);
+ }
+ for (Integer type : reevaluateType) {
+ startProvisionIntent(type);
+ }
+ }
+
+ // The logic here is aimed solely at confirming that a CarrierConfig exists
+ // and affirms that entitlement checks are not required.
+ //
+ // TODO: find a better way to express this, or alter the checking process
+ // entirely so that this is more intuitive.
+ private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
+ // Check carrier config for entitlement checks
+ final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager == null) return false;
+
+ final PersistableBundle carrierConfig = configManager.getConfig();
+ if (carrierConfig == null) return false;
+
+ // A CarrierConfigManager was found and it has a config.
+ final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
+ return !isEntitlementCheckRequired;
+ }
+
+ public void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_RUN_PROVISION, true);
+ intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Used by the SIM card change observation code.
+ // TODO: De-duplicate with above code, where possible.
+ private void startProvisionIntent(int tetherType) {
+ final Intent startProvIntent = new Intent();
+ startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
+ startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
+ startProvIntent.setComponent(TETHER_SERVICE);
+ mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
+ }
+
+ public void scheduleProvisioningRechecks(int type) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_SET_ALARM, true);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void cancelTetherProvisioningRechecks(int type) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 8b40069..d56b167 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -17,19 +17,13 @@
package com.android.server.connectivity.tethering;
import android.content.Context;
-import android.net.INetd;
import android.net.NetworkRequest;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
import android.net.ip.IpServer;
-import android.net.ip.RouterAdvertisementDaemon;
-import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
-import android.os.Handler;
import android.net.util.SharedLog;
-import android.os.Looper;
+import android.os.Handler;
import com.android.internal.util.StateMachine;
+import com.android.server.connectivity.MockableSystemProperties;
import java.util.ArrayList;
@@ -65,4 +59,9 @@
public NetworkRequest getDefaultNetworkRequest() {
return null;
}
+
+ public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
+ MockableSystemProperties systemProperties) {
+ return new EntitlementManager(ctx, log, systemProperties);
+ }
}
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/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e70460a..360a7d1 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -247,9 +247,6 @@
// device).
private Point mStableDisplaySize = new Point();
- // Whether the system has finished booting or not.
- private boolean mSystemReady;
-
// The top inset of the default display.
// This gets persisted so that the boot animation knows how to transition from the display's
// full size to the size configured by the user. Right now we only persist and animate the top
@@ -322,8 +319,6 @@
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
mCurrentUserId = UserHandle.USER_SYSTEM;
-
- mSystemReady = false;
}
public void setupSchedulerPolicies() {
@@ -413,10 +408,6 @@
synchronized (mSyncRoot) {
mSafeMode = safeMode;
mOnlyCore = onlyCore;
- mSystemReady = true;
- // Just in case the top inset changed before the system was ready. At this point, any
- // relevant configuration should be in place.
- recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY));
}
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
@@ -506,7 +497,7 @@
// List is self-synchronized copy-on-write.
for (DisplayTransactionListener listener : mDisplayTransactionListeners) {
- listener.onDisplayTransaction();
+ listener.onDisplayTransaction(t);
}
}
@@ -1065,10 +1056,7 @@
}
private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
- // We must only persist the inset after boot has completed, otherwise we will end up
- // overwriting the persisted value before the masking flag has been loaded from the
- // resource overlay.
- if (!mSystemReady || d == null) {
+ if (d == null) {
return;
}
int topInset = d.getInsets().top;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 6f726e6..9566598 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -57,8 +57,6 @@
* </p>
*/
final class LogicalDisplay {
- private static final String PROP_MASKING_INSET_TOP = "persist.sys.displayinset.top";
-
private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
// The layer stack we use when the display has been blanked to prevent any
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 3a31c9c..3339a49 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -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);
}
@@ -1993,8 +1987,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 9d5d65d..a8da968 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -94,6 +94,7 @@
import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.EventLog;
@@ -179,7 +180,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.WeakHashMap;
@@ -321,7 +321,7 @@
// All known input methods. mMethodMap also serves as the global
// lock for this class.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
- final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<>();
+ final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
private final InputMethodSubtypeSwitchingController mSwitchingController;
@@ -457,7 +457,7 @@
}
}
- final HashMap<IBinder, ClientState> mClients = new HashMap<>();
+ final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
/**
* Set once the system is ready to run third party code.
@@ -553,8 +553,8 @@
private InputMethodSubtype mCurrentSubtype;
// This list contains the pairs of InputMethodInfo and InputMethodSubtype.
- private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
- mShortcutInputMethodsAndSubtypes = new HashMap<>();
+ private final ArrayMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
+ mShortcutInputMethodsAndSubtypes = new ArrayMap<>();
// Was the keyguard locked when this client became current?
private boolean mCurClientInKeyguard;
@@ -1781,7 +1781,9 @@
final int callerPid = Binder.getCallingPid();
synchronized (mMethodMap) {
// TODO: Optimize this linear search.
- for (ClientState state : mClients.values()) {
+ final int numClients = mClients.size();
+ for (int i = 0; i < numClients; ++i) {
+ final ClientState state = mClients.valueAt(i);
if (state.uid == callerUid && state.pid == callerPid
&& state.selfReportedDisplayId == selfReportedDisplayId) {
throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
@@ -2192,8 +2194,9 @@
void clearCurMethodLocked() {
if (mCurMethod != null) {
- for (ClientState cs : mClients.values()) {
- clearClientSessionLocked(cs);
+ final int numClients = mClients.size();
+ for (int i = 0; i < numClients; ++i) {
+ clearClientSessionLocked(mClients.valueAt(i));
}
finishSessionLocked(mEnabledSession);
@@ -2417,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);
}
@@ -3670,7 +3674,7 @@
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS),
mSettings.getCurrentUserId());
- final HashMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+ final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
mFileManager.getAllAdditionalInputMethodSubtypes();
for (int i = 0; i < services.size(); ++i) {
ResolveInfo ri = services.get(i);
@@ -3824,19 +3828,15 @@
if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
synchronized (mMethodMap) {
- final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
- mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
- mContext);
- if (immis == null || immis.size() == 0) {
+ final List<ImeSubtypeListItem> imList =
+ mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
+ showAuxSubtypes, isScreenLocked);
+ if (imList.isEmpty()) {
return;
}
hideInputMethodMenuLocked();
- final List<ImeSubtypeListItem> imList =
- mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
- showAuxSubtypes, isScreenLocked);
-
if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
if (currentSubtype != null) {
@@ -4310,10 +4310,10 @@
private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
private static final String ATTR_IS_ASCII_CAPABLE = "isAsciiCapable";
private final AtomicFile mAdditionalInputMethodSubtypeFile;
- private final HashMap<String, InputMethodInfo> mMethodMap;
- private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
- new HashMap<>();
- public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap, int userId) {
+ private final ArrayMap<String, InputMethodInfo> mMethodMap;
+ private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
+ new ArrayMap<>();
+ InputMethodFileManager(ArrayMap<String, InputMethodInfo> methodMap, int userId) {
if (methodMap == null) {
throw new NullPointerException("methodMap is null");
}
@@ -4365,15 +4365,15 @@
}
}
- public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
+ public ArrayMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
synchronized (mMethodMap) {
return mAdditionalSubtypesMap;
}
}
private static void writeAdditionalInputMethodSubtypes(
- HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
- HashMap<String, InputMethodInfo> methodMap) {
+ ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
+ ArrayMap<String, InputMethodInfo> methodMap) {
// Safety net for the case that this function is called before methodMap is set.
final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
FileOutputStream fos = null;
@@ -4427,7 +4427,7 @@
}
private static void readAdditionalInputMethodSubtypes(
- HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
+ ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
if (allSubtypes == null || subtypesFile == null) return;
allSubtypes.clear();
try (final FileInputStream fis = subtypesFile.openRead()) {
@@ -4625,7 +4625,9 @@
info.dump(p, " ");
}
p.println(" Clients:");
- for (ClientState ci : mClients.values()) {
+ final int numClients = mClients.size();
+ for (int i = 0; i < numClients; ++i) {
+ final ClientState ci = mClients.valueAt(i);
p.println(" Client " + ci + ":");
p.println(" client=" + ci.client);
p.println(" inputContext=" + ci.inputContext);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 27c59d4..b13c307 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Printer;
import android.util.Slog;
@@ -31,8 +32,6 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -184,11 +183,8 @@
public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
boolean includeAuxiliarySubtypes, boolean isScreenLocked) {
- final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
- final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
- mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
- mContext);
- if (immis == null || immis.size() == 0) {
+ final ArrayList<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
+ if (imis.isEmpty()) {
return Collections.emptyList();
}
if (isScreenLocked && includeAuxiliarySubtypes) {
@@ -197,12 +193,13 @@
}
includeAuxiliarySubtypes = false;
}
- for (InputMethodInfo imi : immis.keySet()) {
- if (imi == null) {
- continue;
- }
- List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
- HashSet<String> enabledSubtypeSet = new HashSet<>();
+ final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
+ final int numImes = imis.size();
+ for (int i = 0; i < numImes; ++i) {
+ final InputMethodInfo imi = imis.get(i);
+ final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
+ mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
+ final ArraySet<String> enabledSubtypeSet = new ArraySet<>();
for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 154e8b3..8e3f351 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Pair;
import android.util.Printer;
import android.util.Slog;
@@ -45,7 +46,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
@@ -473,7 +473,7 @@
final int numSubtypes = subtypes.size();
// Handle overridesImplicitlyEnabledSubtype mechanism.
- final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new HashMap<>();
+ final ArrayMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new ArrayMap<>();
for (int i = 0; i < numSubtypes; ++i) {
// scan overriding implicitly enabled subtypes.
final InputMethodSubtype subtype = subtypes.get(i);
@@ -488,8 +488,8 @@
return new ArrayList<>(applicableModeAndSubtypesMap.values());
}
- final HashMap<String, ArrayList<InputMethodSubtype>> nonKeyboardSubtypesMap =
- new HashMap<>();
+ final ArrayMap<String, ArrayList<InputMethodSubtype>> nonKeyboardSubtypesMap =
+ new ArrayMap<>();
final ArrayList<InputMethodSubtype> keyboardSubtypes = new ArrayList<>();
for (int i = 0; i < numSubtypes; ++i) {
@@ -761,12 +761,12 @@
private final Resources mRes;
private final ContentResolver mResolver;
- private final HashMap<String, InputMethodInfo> mMethodMap;
+ private final ArrayMap<String, InputMethodInfo> mMethodMap;
/**
* On-memory data store to emulate when {@link #mCopyOnWrite} is {@code true}.
*/
- private final HashMap<String, String> mCopyOnWriteDataStore = new HashMap<>();
+ private final ArrayMap<String, String> mCopyOnWriteDataStore = new ArrayMap<>();
private boolean mCopyOnWrite = false;
@NonNull
@@ -812,7 +812,7 @@
public InputMethodSettings(
Resources res, ContentResolver resolver,
- HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
+ ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
@UserIdInt int userId, boolean copyOnWrite) {
mRes = res;
mResolver = resolver;
@@ -1277,17 +1277,6 @@
}
}
- public HashMap<InputMethodInfo, List<InputMethodSubtype>>
- getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(Context context) {
- HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes =
- new HashMap<>();
- for (InputMethodInfo imi: getEnabledInputMethodListLocked()) {
- enabledInputMethodAndSubtypes.put(
- imi, getEnabledInputMethodSubtypeListLocked(context, imi, true));
- }
- return enabledInputMethodAndSubtypes;
- }
-
public void dumpLocked(final Printer pw, final String prefix) {
pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
pw.println(prefix + "mCurrentProfileIds=" + Arrays.toString(mCurrentProfileIds));
diff --git a/services/core/java/com/android/server/inputmethod/LocaleUtils.java b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
index 4958ece..7a6853a 100644
--- a/services/core/java/com/android/server/inputmethod/LocaleUtils.java
+++ b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
@@ -22,10 +22,10 @@
import android.icu.util.ULocale;
import android.os.LocaleList;
import android.text.TextUtils;
+import android.util.ArrayMap;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -155,7 +155,7 @@
}
final int numPreferredLocales = preferredLocales.size();
- final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
+ final ArrayMap<String, ScoreEntry> scoreboard = new ArrayMap<>();
final byte[] score = new byte[numPreferredLocales];
final ULocale[] preferredULocaleCache = new ULocale[numPreferredLocales];
@@ -197,7 +197,11 @@
}
}
- final ScoreEntry[] result = scoreboard.values().toArray(new ScoreEntry[scoreboard.size()]);
+ final int numEntries = scoreboard.size();
+ final ScoreEntry[] result = new ScoreEntry[numEntries];
+ for (int i = 0; i < numEntries; ++i) {
+ result[i] = scoreboard.valueAt(i);
+ }
Arrays.sort(result);
for (final ScoreEntry entry : result) {
dest.add(sources.get(entry.mIndex));
diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
index aac83b6..6fe6324 100644
--- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
+++ b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
@@ -19,6 +19,8 @@
import android.annotation.UserIdInt;
import android.os.Bundle;
import android.os.IBinder;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
/**
* Intelligence Manager local system service interface.
@@ -41,4 +43,37 @@
*/
public abstract boolean sendActivityAssistData(@UserIdInt int userId,
@NonNull IBinder activityToken, @NonNull Bundle data);
+
+ /**
+ * Asks the intelligence service to provide Augmented Autofill for a given activity.
+ *
+ * @param userId user handle
+ * @param client binder used to communicate with the activity that originated this request.
+ * @param activityToken activity that originated this request.
+ * @param autofillSessionId autofill session id (must be used on {@code client} calls.
+ * @param focusedId id of the the field that triggered this request.
+ *
+ * @return {@code false} if the service cannot handle this request, {@code true} otherwise.
+ * <b>NOTE: </b> it must return right away; typically it will return {@code false} if the
+ * service is disabled (or the activity blacklisted).
+ */
+ public abstract AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
+ @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
+ int autofillSessionId, @NonNull AutofillId focusedId);
+
+ /**
+ * Callback used by the Autofill Session to communicate with the Augmented Autofill service.
+ */
+ public interface AugmentedAutofillCallback {
+ // TODO(b/111330312): this method is calling when the Autofill session is destroyed, the
+ // main reason being the cases where user tap HOME.
+ // Right now it's completely destroying the UI, but we need to decide whether / how to
+ // properly recover it later (for example, if the user switches back to the activity,
+ // should it be restored? Right not it kind of is, because Autofill's Session trigger a
+ // new FillRequest, which in turn triggers the Augmented Autofill request again)
+ /**
+ * Destroys the Autofill UI.
+ */
+ void destroy();
+ }
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 9e6e381..d5e4681 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -97,8 +97,8 @@
*
* {@hide}
*/
-public class GnssLocationProvider implements LocationProviderInterface, InjectNtpTimeCallback,
- GnssSatelliteBlacklistCallback {
+public class GnssLocationProvider extends LocationProviderInterface
+ implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback {
private static final String TAG = "GnssLocationProvider";
diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java
index 6f09232..6785964 100644
--- a/services/core/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/core/java/com/android/server/location/LocationProviderInterface.java
@@ -16,33 +16,63 @@
package com.android.server.location;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.os.WorkSource;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-
-import android.os.Bundle;
-import android.os.WorkSource;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* Location Manager's interface for location providers.
* @hide
*/
-public interface LocationProviderInterface {
- public String getName();
+public abstract class LocationProviderInterface {
- public void enable();
- public void disable();
- public boolean isEnabled();
- public void setRequest(ProviderRequest request, WorkSource source);
+ /** Get name. */
+ public abstract String getName();
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+ /** Enable. */
+ public abstract void enable();
- // --- deprecated (but still supported) ---
- public ProviderProperties getProperties();
- public int getStatus(Bundle extras);
- public long getStatusUpdateTime();
- public boolean sendExtraCommand(String command, Bundle extras);
+ /** Disable. */
+ public abstract void disable();
+
+ /** Is enabled. */
+ public abstract boolean isEnabled();
+
+ /** Set request. */
+ public abstract void setRequest(ProviderRequest request, WorkSource source);
+
+ /** dump. */
+ public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+ /** Get properties. */
+ public abstract ProviderProperties getProperties();
+
+ /**
+ * Get status.
+ *
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
+ public int getStatus(Bundle extras) {
+ return LocationProvider.AVAILABLE;
+ }
+
+ /**
+ * Get status update time.
+ *
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
+ public long getStatusUpdateTime() {
+ return 0;
+ }
+
+ /** Send extra command. */
+ public abstract boolean sendExtraCommand(String command, Bundle extras);
}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index bb86b48..b408414 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -41,7 +41,7 @@
/**
* Proxy for ILocationProvider implementations.
*/
-public class LocationProviderProxy implements LocationProviderInterface {
+public class LocationProviderProxy extends LocationProviderInterface {
private static final String TAG = "LocationProviderProxy";
private static final boolean D = LocationManagerService.D;
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 8578761..145aee3 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -25,31 +25,31 @@
import android.util.Log;
import android.util.PrintWriterPrinter;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
/**
* A mock location provider used by LocationManagerService to implement test providers.
*
* {@hide}
*/
-public class MockProvider implements LocationProviderInterface {
+public class MockProvider extends LocationProviderInterface {
private final String mName;
private final ProviderProperties mProperties;
private final ILocationManager mLocationManager;
private final Location mLocation;
- private final Bundle mExtras = new Bundle();
+
+ private boolean mHasLocation;
+ private boolean mEnabled;
+
private int mStatus;
private long mStatusUpdateTime;
- private boolean mHasLocation;
- private boolean mHasStatus;
- private boolean mEnabled;
+ private Bundle mExtras;
private static final String TAG = "MockProvider";
@@ -61,6 +61,10 @@
mLocationManager = locationManager;
mProperties = properties;
mLocation = new Location(name);
+
+ mStatus = LocationProvider.AVAILABLE;
+ mStatusUpdateTime = 0L;
+ mExtras = null;
}
@Override
@@ -90,13 +94,12 @@
@Override
public int getStatus(Bundle extras) {
- if (mHasStatus) {
+ if (mExtras != null) {
extras.clear();
extras.putAll(mExtras);
- return mStatus;
- } else {
- return LocationProvider.AVAILABLE;
}
+
+ return mStatus;
}
@Override
@@ -120,19 +123,14 @@
mHasLocation = false;
}
+ /**
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
public void setStatus(int status, Bundle extras, long updateTime) {
mStatus = status;
mStatusUpdateTime = updateTime;
- mExtras.clear();
- if (extras != null) {
- mExtras.putAll(extras);
- }
- mHasStatus = true;
- }
-
- public void clearStatus() {
- mHasStatus = false;
- mStatusUpdateTime = 0;
+ mExtras = extras;
}
@Override
@@ -145,9 +143,6 @@
pw.println(prefix + "mHasLocation=" + mHasLocation);
pw.println(prefix + "mLocation:");
mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
- pw.println(prefix + "mHasStatus=" + mHasStatus);
- pw.println(prefix + "mStatus=" + mStatus);
- pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
pw.println(prefix + "mExtras=" + mExtras);
}
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 71bae07..99c9214 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -16,22 +16,20 @@
package com.android.server.location;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
-import android.location.LocationProvider;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* A passive location provider reports locations received from other providers
@@ -40,7 +38,7 @@
*
* {@hide}
*/
-public class PassiveProvider implements LocationProviderInterface {
+public class PassiveProvider extends LocationProviderInterface {
private static final String TAG = "PassiveProvider";
private static final ProviderProperties PROPERTIES = new ProviderProperties(
@@ -78,20 +76,6 @@
}
@Override
- public int getStatus(Bundle extras) {
- if (mReportLocation) {
- return LocationProvider.AVAILABLE;
- } else {
- return LocationProvider.TEMPORARILY_UNAVAILABLE;
- }
- }
-
- @Override
- public long getStatusUpdateTime() {
- return -1;
- }
-
- @Override
public void setRequest(ProviderRequest request, WorkSource source) {
mReportLocation = request.reportLocation;
}
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 9d402b3..c18a79f 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -16,10 +16,7 @@
package com.android.server.media;
-import static android.media.SessionToken2.TYPE_SESSION;
-
import android.app.ActivityManager;
-import android.app.AppGlobals;
import android.app.INotificationManager;
import android.app.KeyguardManager;
import android.app.PendingIntent;
@@ -30,7 +27,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
@@ -40,9 +36,6 @@
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.IRemoteVolumeController;
-import android.media.ISessionTokensListener;
-import android.media.MediaController2;
-import android.media.SessionToken2;
import android.media.session.IActiveSessionsListener;
import android.media.session.ICallback;
import android.media.session.IOnMediaKeyListener;
@@ -68,7 +61,6 @@
import android.provider.Settings;
import android.speech.RecognizerIntent;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -85,15 +77,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
/**
* System implementation of MediaSessionManager
*/
public class MediaSessionService extends SystemService implements Monitor {
private static final String TAG = "MediaSessionService";
- static final boolean USE_MEDIA2_APIS = false; // TODO: Change this to true when we're ready.
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// Leave log for key event always.
private static final boolean DEBUG_KEY_EVENT = true;
@@ -113,7 +102,6 @@
private final PowerManager.WakeLock mMediaEventWakeLock;
private final int mLongPressTimeout;
private final INotificationManager mNotificationManager;
- private final IPackageManager mPackageManager;
private KeyguardManager mKeyguardManager;
private IAudioService mAudioService;
@@ -131,13 +119,6 @@
// better way to handle this.
private IRemoteVolumeController mRvc;
- // MediaSession2 support
- // TODO(jaewan): Support multi-user and managed profile. (b/73597722)
- // TODO(jaewan): Make it priority list for handling volume/media key. (b/73760382)
- private final Map<SessionToken2, MediaController2> mSessionRecords = new ArrayMap<>();
-
- private final List<SessionTokensListenerRecord> mSessionTokensListeners = new ArrayList<>();
-
public MediaSessionService(Context context) {
super(context);
mSessionManagerImpl = new SessionManagerImpl();
@@ -146,7 +127,6 @@
mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
mNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
- mPackageManager = AppGlobals.getPackageManager();
}
@Override
@@ -645,20 +625,6 @@
return mUserRecords.get(fullUserId);
}
- void destroySession2Internal(SessionToken2 token) {
- synchronized (mLock) {
- boolean notifySessionTokensUpdated = false;
- if (token.getType() == SessionToken2.TYPE_SESSION) {
- notifySessionTokensUpdated |= removeSessionRecordLocked(token);
- } else {
- notifySessionTokensUpdated |= addSessionRecordLocked(token);
- }
- if (notifySessionTokensUpdated) {
- postSessionTokensUpdated(UserHandle.getUserId(token.getUid()));
- }
- }
- }
-
/**
* Information about a full user and its corresponding managed profiles.
*
@@ -1417,163 +1383,6 @@
}
}
- /**
- * Called when a {@link android.media.MediaSession2} instance is created.
- * <p>
- * This does two things.
- * 1. Keep the newly created session in the service
- * 2. Do sanity check to ensure unique id per package, and return result
- *
- * @param sessionToken SessionToken2 object in bundled form
- * @return {@code true} if the session's id isn't used by the package now. {@code false}
- * otherwise.
- */
- @Override
- public boolean createSession2(Bundle sessionToken) {
- if (!USE_MEDIA2_APIS) {
- return false;
- }
- final int uid = Binder.getCallingUid();
- final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
- if (token == null || token.getUid() != uid) {
- Log.w(TAG, "onSessionCreated failed, expected caller uid=" + token.getUid()
- + " but from uid=" + uid);
- }
- if (DEBUG) {
- Log.d(TAG, "createSession2: " + token);
- }
- synchronized (mLock) {
- MediaController2 controller = mSessionRecords.get(token);
- if (controller != null && controller.isConnected()) {
- return false;
- }
- Context context = getContext();
- controller = new MediaController2(context, token, context.getMainExecutor(),
- new ControllerCallback(token));
- if (addSessionRecordLocked(token, controller)) {
- postSessionTokensUpdated(UserHandle.getUserId(token.getUid()));
- }
- return true;
- }
- }
-
- /**
- * Called when a {@link android.media.MediaSession2} instance is closed. (i.e. destroyed)
- * <p>
- * Ideally service should know that a session is destroyed through the
- * {@link android.media.MediaController2.ControllerCallback#onDisconnected()}, which is
- * asynchronous call. However, we also need synchronous way together to address timing
- * issue. If the package recreates the session almost immediately, which happens commonly
- * for tests, service will reject the creation through {@link #onSessionCreated(Bundle)}
- * if the service hasn't notified previous destroy yet. This synchronous API will address
- * the issue.
- *
- * @param sessionToken SessionToken2 object in bundled form
- */
- @Override
- public void destroySession2(Bundle sessionToken) {
- if (!USE_MEDIA2_APIS) {
- return;
- }
- final int uid = Binder.getCallingUid();
- final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
- if (token == null || token.getUid() != uid) {
- Log.w(TAG, "onSessionDestroyed failed, expected caller uid=" + token.getUid()
- + " but from uid=" + uid);
- }
- if (DEBUG) {
- Log.d(TAG, "destroySession2 " + token);
- }
- destroySession2Internal(token);
- }
-
- // TODO(jaewan): Make this API take userId as an argument (b/73597722)
- @Override
- public List<Bundle> getSessionTokens(boolean activeSessionOnly,
- boolean sessionServiceOnly, String packageName) throws RemoteException {
- if (!USE_MEDIA2_APIS) {
- return null;
- }
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
-
- List<Bundle> tokens = new ArrayList<>();
- try {
- verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
- synchronized (mLock) {
- for (Map.Entry<SessionToken2, MediaController2> record
- : mSessionRecords.entrySet()) {
- boolean isSessionService = (record.getKey().getType() != TYPE_SESSION);
- boolean isActive = record.getValue() != null;
- if ((activeSessionOnly && !isActive)
- || (sessionServiceOnly && !isSessionService)) {
- continue;
- }
- tokens.add(record.getKey().toBundle());
- }
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return tokens;
- }
-
- @Override
- public void addSessionTokensListener(ISessionTokensListener listener, int userId,
- String packageName) throws RemoteException {
- if (!USE_MEDIA2_APIS) {
- return;
- }
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- try {
- int resolvedUserId = verifySessionsRequest2(userId, packageName, pid, uid);
- synchronized (mLock) {
- final SessionTokensListenerRecord record =
- new SessionTokensListenerRecord(listener, resolvedUserId);
- try {
- listener.asBinder().linkToDeath(record, 0);
- } catch (RemoteException e) {
- }
- mSessionTokensListeners.add(record);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- // TODO(jaewan): Make this API take userId as an argument (b/73597722)
- @Override
- public void removeSessionTokensListener(ISessionTokensListener listener,
- String packageName) throws RemoteException {
- if (!USE_MEDIA2_APIS) {
- return;
- }
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- try {
- verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
- synchronized (mLock) {
- IBinder listenerBinder = listener.asBinder();
- for (SessionTokensListenerRecord record : mSessionTokensListeners) {
- if (listenerBinder.equals(record.mListener.asBinder())) {
- try {
- listenerBinder.unlinkToDeath(record, 0);
- } catch (NoSuchElementException e) {
- }
- mSessionTokensListeners.remove(record);
- break;
- }
- }
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
// For MediaSession
private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
final int uid) {
@@ -1594,23 +1403,6 @@
return resolvedUserId;
}
- // For MediaSession2
- private int verifySessionsRequest2(int targetUserId, String callerPackageName,
- int callerPid, int callerUid) throws RemoteException {
- // Check that they can make calls on behalf of the user and get the final user id.
- int resolvedUserId = ActivityManager.handleIncomingUser(callerPid, callerUid,
- targetUserId, true /* allowAll */, true /* requireFull */, "getSessionTokens",
- callerPackageName);
- // Check if they have the permissions or their component is
- // enabled for the user they're calling from.
- if (!hasMediaControlPermission(
- resolvedUserId, callerPackageName, callerPid, callerUid)) {
- throw new SecurityException("Missing permission to control media.");
- }
- return resolvedUserId;
- }
-
- // For MediaSession2
private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
int pid, int uid) throws RemoteException {
// Allow API calls from the System UI
@@ -2014,7 +1806,6 @@
final class MessageHandler extends Handler {
private static final int MSG_SESSIONS_CHANGED = 1;
private static final int MSG_VOLUME_INITIAL_DOWN = 2;
- private static final int MSG_SESSIONS_TOKENS_CHANGED = 3;
private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
@Override
@@ -2033,9 +1824,6 @@
}
}
break;
- case MSG_SESSIONS_TOKENS_CHANGED:
- pushSessionTokensChanged((int) msg.obj);
- break;
}
}
@@ -2050,86 +1838,4 @@
obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
}
}
-
- private class ControllerCallback extends MediaController2.ControllerCallback {
-
- private final SessionToken2 mToken;
-
- ControllerCallback(SessionToken2 token) {
- mToken = token;
- }
-
- @Override
- public void onDisconnected(MediaController2 controller) {
- destroySession2Internal(mToken);
- }
- };
-
- private final class SessionTokensListenerRecord implements IBinder.DeathRecipient {
- private final ISessionTokensListener mListener;
- private final int mUserId;
-
- public SessionTokensListenerRecord(ISessionTokensListener listener, int userId) {
- mListener = listener;
- // TODO(jaewan): should userId be mapped through mFullUserIds? (b/73597722)
- mUserId = userId;
- }
-
- @Override
- public void binderDied() {
- synchronized (mLock) {
- mSessionTokensListeners.remove(this);
- }
- }
- }
-
- private void postSessionTokensUpdated(int userId) {
- mHandler.obtainMessage(MessageHandler.MSG_SESSIONS_TOKENS_CHANGED, userId).sendToTarget();
- }
-
- private void pushSessionTokensChanged(int userId) {
- synchronized (mLock) {
- List<Bundle> tokens = new ArrayList<>();
- for (SessionToken2 token : mSessionRecords.keySet()) {
- if (UserHandle.getUserId(token.getUid()) == userId) {
- tokens.add(token.toBundle());
- }
- }
-
- for (SessionTokensListenerRecord record : mSessionTokensListeners) {
- // TODO(jaewan): Should userId be mapped through mFullUserIds? (b/73760382)
- if (record.mUserId == userId || record.mUserId == UserHandle.USER_ALL) {
- try {
- record.mListener.onSessionTokensChanged(tokens);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to notify session tokens changed", e);
- }
- }
- }
- }
- }
-
- private boolean addSessionRecordLocked(SessionToken2 token) {
- return addSessionRecordLocked(token, null);
- }
-
- private boolean addSessionRecordLocked(SessionToken2 token, MediaController2 controller) {
- if (mSessionRecords.containsKey(token) && mSessionRecords.get(token) == controller) {
- // The key/value pair already exists, no need to update.
- return false;
- }
-
- mSessionRecords.put(token, controller);
- return true;
- }
-
- private boolean removeSessionRecordLocked(SessionToken2 token) {
- if (!mSessionRecords.containsKey(token)) {
- // The key is already removed, no need to remove.
- return false;
- }
-
- mSessionRecords.remove(token);
- return true;
- }
}
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..9ed978f 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,59 @@
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) {
+ 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 +4022,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 +4067,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 +4277,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 +4844,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 +5106,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 decdac6..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,
@@ -45,5 +46,15 @@
void onNotificationDirectReplied(String key);
void onNotificationSettingsViewed(String key);
void onNotificationSmartRepliesAdded(String key, int replyCount);
- void onNotificationSmartReplySent(String key, int replyIndex);
+
+ /**
+ * Notifies a smart reply is sent.
+ *
+ * @param key the notification key
+ * @param clickedIndex the index of clicked reply
+ * @param reply the reply that is sent
+ * @param generatedByAssistant specifies is the reply generated by NAS
+ */
+ void onNotificationSmartReplySent(String key, int clickedIndex, CharSequence reply,
+ boolean generatedByAssistant);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 32990ce..ae27d0c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -246,6 +246,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
import java.util.function.Predicate;
/** {@hide} */
@@ -411,7 +412,7 @@
private NotificationAssistants mAssistants;
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
- private boolean mLockScreenAllowSecureNotifications;
+ private boolean mLockScreenAllowSecureNotifications = true;
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
@@ -751,7 +752,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);
@@ -771,6 +773,8 @@
nv.rank, nv.count);
nv.recycle();
reportUserInteraction(r);
+ mAssistants.notifyAssistantActionClicked(
+ r.sbn, actionIndex, action, generatedByAssistant);
}
}
@@ -887,6 +891,7 @@
EventLogTags.writeNotificationExpansion(key,
userAction ? 1 : 0, expanded ? 1 : 0,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
+ mAssistants.notifyAssistantExpansionChangedLocked(r.sbn, userAction, expanded);
}
}
}
@@ -902,6 +907,7 @@
.setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
.setType(MetricsEvent.TYPE_ACTION));
reportUserInteraction(r);
+ mAssistants.notifyAssistantNotificationDirectReplyLocked(r.sbn);
}
}
}
@@ -917,7 +923,8 @@
}
@Override
- public void onNotificationSmartReplySent(String key, int replyIndex) {
+ public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
+ boolean generatedByAssistant) {
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
@@ -927,6 +934,8 @@
mMetricsLogger.write(logMaker);
// Treat clicking on a smart reply as a user interaction.
reportUserInteraction(r);
+ mAssistants.notifyAssistantSuggestedReplySent(
+ r.sbn, reply, generatedByAssistant);
}
}
}
@@ -4398,19 +4407,20 @@
*
* Has side effects.
*/
- private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
+ private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
NotificationRecord r, boolean isAutogroup) {
final String pkg = r.sbn.getPackageName();
final boolean isSystemNotification =
- isUidSystemOrPhone(callingUid) || ("android".equals(pkg));
+ isUidSystemOrPhone(uid) || ("android".equals(pkg));
final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
// Limit the number of notifications that any given package except the android
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationLock) {
+ final int callingUid = Binder.getCallingUid();
if (mNotificationsByKey.get(r.sbn.getKey()) == null
- && isCallerInstantApp(pkg, Binder.getCallingUid(), userId)) {
+ && isCallerInstantApp(callingUid, userId)) {
// Ephemeral apps have some special constraints for notifications.
// They are not allowed to create new notifications however they are allowed to
// update notifications created by the system (e.g. a foreground service
@@ -4729,7 +4739,7 @@
mRankingHelper.extractSignals(r);
// tell the assistant service about the notification
if (mAssistants.isEnabled()) {
- mAssistants.onNotificationEnqueued(r);
+ mAssistants.onNotificationEnqueuedLocked(r);
mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
DELAY_FOR_ASSISTANT_TIME);
} else {
@@ -6521,24 +6531,28 @@
}
@VisibleForTesting
- boolean isCallerInstantApp(String pkg, int callingUid, int userId) {
+ boolean isCallerInstantApp(int callingUid, int userId) {
// System is always allowed to act for ephemeral apps.
if (isUidSystemOrPhone(callingUid)) {
return false;
}
- mAppOps.checkPackage(callingUid, pkg);
-
try {
+ final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
+ if (pkgs == null) {
+ throw new SecurityException("Unknown uid " + callingUid);
+ }
+ final String pkg = pkgs[0];
+ mAppOps.checkPackage(callingUid, pkg);
+
ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId);
if (ai == null) {
throw new SecurityException("Unknown package " + pkg);
}
return ai.isInstantApp();
} catch (RemoteException re) {
- throw new SecurityException("Unknown package " + pkg, re);
+ throw new SecurityException("Unknown uid " + callingUid, re);
}
-
}
private void checkCallerIsSameApp(String pkg) {
@@ -6842,38 +6856,96 @@
}
}
- public void onNotificationEnqueued(final NotificationRecord r) {
+ @GuardedBy("mNotificationLock")
+ private void onNotificationEnqueuedLocked(final NotificationRecord r) {
final StatusBarNotification sbn = r.sbn;
- TrimCache trimCache = new TrimCache(sbn);
-
- // There should be only one, but it's a list, so while we enforce
- // singularity elsewhere, we keep it general here, to avoid surprises.
- for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, info)
- && info.isSameUser(r.getUserId());
- if (!sbnVisible) {
- continue;
- }
-
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- notifyEnqueued(info, sbnToPost, r.getChannel());
- }
- });
- }
+ notifyAssistantLocked(
+ sbn,
+ true /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel());
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+ }
+ });
}
- private void notifyEnqueued(final ManagedServiceInfo info,
- final StatusBarNotification sbn, final NotificationChannel channel) {
- final INotificationListener assistant = (INotificationListener) info.service;
- StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
- try {
- assistant.onNotificationEnqueuedWithChannel(sbnHolder, channel);
- } catch (RemoteException ex) {
- Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
- }
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantExpansionChangedLocked(
+ final StatusBarNotification sbn,
+ final boolean isUserAction,
+ final boolean isExpanded) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationExpansionChanged(key, isUserAction, isExpanded);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+ }
+ });
+ }
+
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantNotificationDirectReplyLocked(
+ final StatusBarNotification sbn) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationDirectReply(key);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+ }
+ });
+ }
+
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantSuggestedReplySent(
+ final StatusBarNotification sbn, CharSequence reply, boolean generatedByAssistant) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onSuggestedReplySent(
+ key,
+ reply,
+ generatedByAssistant
+ ? NotificationAssistantService.SOURCE_FROM_ASSISTANT
+ : NotificationAssistantService.SOURCE_FROM_APP);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
+ }
+ });
+ }
+
+ @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);
+ }
+ });
}
/**
@@ -6881,30 +6953,49 @@
* context
*/
@GuardedBy("mNotificationLock")
- public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
- final String snoozeCriterionId) {
- TrimCache trimCache = new TrimCache(sbn);
- for (final ManagedServiceInfo info : getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, info);
- if (!sbnVisible) {
- continue;
- }
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final INotificationListener assistant =
- (INotificationListener) info.service;
- StatusBarNotificationHolder sbnHolder
- = new StatusBarNotificationHolder(sbnToPost);
+ private void notifyAssistantSnoozedLocked(
+ final StatusBarNotification sbn, final String snoozeCriterionId) {
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
try {
assistant.onNotificationSnoozedUntilContext(
sbnHolder, snoozeCriterionId);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
}
- }
- });
+ });
+ }
+
+ /**
+ * Notifies the assistant something about the specified notification, only assistant
+ * that is visible to the notification will be notified.
+ *
+ * @param sbn the notification object that the update is about.
+ * @param sameUserOnly should the update be sent to the assistant in the same user only.
+ * @param callback the callback that provides the assistant to be notified, executed
+ * in WorkerHandler.
+ */
+ @GuardedBy("mNotificationLock")
+ private void notifyAssistantLocked(
+ final StatusBarNotification sbn,
+ boolean sameUserOnly,
+ BiConsumer<INotificationListener, StatusBarNotificationHolder> callback) {
+ TrimCache trimCache = new TrimCache(sbn);
+ // There should be only one, but it's a list, so while we enforce
+ // singularity elsewhere, we keep it general here, to avoid surprises.
+ for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+ boolean sbnVisible = isVisibleToListener(sbn, info)
+ && (!sameUserOnly || info.isSameUser(sbn.getUserId()));
+ if (!sbnVisible) {
+ continue;
+ }
+ final INotificationListener assistant = (INotificationListener) info.service;
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+ final StatusBarNotificationHolder sbnHolder =
+ new StatusBarNotificationHolder(sbnToPost);
+ mHandler.post(() -> callback.accept(assistant, sbnHolder));
}
}
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/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/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f1b03d1..d471904 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -22,14 +22,11 @@
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
-import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityThread;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -37,7 +34,6 @@
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
@@ -59,11 +55,9 @@
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.util.ConcurrentUtils;
import com.android.server.FgThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
-import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.pm.Installer;
import com.android.server.pm.UserManagerService;
@@ -84,8 +78,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -228,8 +220,6 @@
private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
- private Future<?> mInitCompleteSignal;
-
public OverlayManagerService(@NonNull final Context context,
@NonNull final Installer installer) {
super(context);
@@ -241,29 +231,28 @@
mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
getDefaultOverlayPackages(), new OverlayChangeListener());
- mInitCompleteSignal = SystemServerInitThreadPool.get().submit(() -> {
- final IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(ACTION_PACKAGE_ADDED);
- packageFilter.addAction(ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
- getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
- packageFilter, null, null);
- final IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(ACTION_USER_ADDED);
- userFilter.addAction(ACTION_USER_REMOVED);
- getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
- userFilter, null, null);
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+ getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
+ packageFilter, null, null);
- restoreSettings();
+ final IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(ACTION_USER_ADDED);
+ userFilter.addAction(ACTION_USER_REMOVED);
+ getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
+ userFilter, null, null);
- initIfNeeded();
- onSwitchUser(UserHandle.USER_SYSTEM);
+ restoreSettings();
- publishBinderService(Context.OVERLAY_SERVICE, mService);
- publishLocalService(OverlayManagerService.class, this);
- }, "Init OverlayManagerService");
+ initIfNeeded();
+ onSwitchUser(UserHandle.USER_SYSTEM);
+
+ publishBinderService(Context.OVERLAY_SERVICE, mService);
+ publishLocalService(OverlayManagerService.class, this);
}
@Override
@@ -271,32 +260,6 @@
// Intentionally left empty.
}
- @Override
- public void onBootPhase(int phase) {
- if (phase == PHASE_SYSTEM_SERVICES_READY && mInitCompleteSignal != null) {
- ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
- "Wait for OverlayManagerService init");
- mInitCompleteSignal = null;
- }
- }
-
- public void updateSystemUiContext() {
- if (mInitCompleteSignal != null) {
- ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
- "Wait for OverlayManagerService init");
- mInitCompleteSignal = null;
- }
-
- final ApplicationInfo ai;
- try {
- ai = mPackageManager.mPackageManager.getApplicationInfo("android",
- GET_SHARED_LIBRARY_FILES, UserHandle.USER_SYSTEM);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- ActivityThread.currentActivityThread().handleSystemApplicationInfoChanged(ai);
- }
-
private void initIfNeeded() {
final UserManager um = getContext().getSystemService(UserManager.class);
final List<UserInfo> users = um.getUsers(true /*excludeDying*/);
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/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 5810e30..b52c021 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -333,9 +333,7 @@
PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
collectingInstaller, mPackageManagerService.mInstallLock, mContext);
- String[] libraryDependencies = pkg.usesLibraryFiles;
-
- optimizer.performDexOpt(pkg, libraryDependencies,
+ optimizer.performDexOpt(pkg, pkg.usesLibraryInfos,
null /* ISAs */,
null /* CompilerStats.PackageStats */,
mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 95d2154..093b85e 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -16,10 +16,33 @@
package com.android.server.pm;
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+
+import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
+import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
+import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
+import static com.android.server.pm.Installer.DEXOPT_FORCE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
+import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
+import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
+import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
+
+import static dalvik.system.DexFile.getSafeModeCompilerFilter;
+import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
+
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
+import android.content.pm.SharedLibraryInfo;
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.os.FileUtils;
@@ -40,6 +63,8 @@
import com.android.server.pm.dex.DexoptUtils;
import com.android.server.pm.dex.PackageDexUsage;
+import dalvik.system.DexFile;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -47,32 +72,6 @@
import java.util.List;
import java.util.Map;
-import dalvik.system.DexFile;
-
-import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
-
-import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
-import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
-import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
-import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
-import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
-import static com.android.server.pm.Installer.DEXOPT_FORCE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
-import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
-import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
-
-import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
-
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
-
-import static dalvik.system.DexFile.getSafeModeCompilerFilter;
-import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
-
/**
* Helper class for running dexopt command on packages.
*/
@@ -129,7 +128,7 @@
* <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
* synchronized on {@link #mInstallLock}.
*/
- int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
+ int performDexOpt(PackageParser.Package pkg, List<SharedLibraryInfo> sharedLibraries,
String[] instructionSets, CompilerStats.PackageStats packageStats,
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
if (pkg.applicationInfo.uid == -1) {
@@ -155,7 +154,8 @@
* It assumes the install lock is held.
*/
@GuardedBy("mInstallLock")
- private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
+ private int performDexOptLI(PackageParser.Package pkg,
+ List<SharedLibraryInfo> sharedLibraries,
String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
final String[] instructionSets = targetInstructionSets != null ?
@@ -542,8 +542,7 @@
// The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
// the user does not have an existing profile.
boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
- boolean isPublic = !info.isForwardLocked() &&
- (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
+ boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata();
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
// Some apps are executed with restrictions on hidden API usage. If this app is one
// of them, pass a flag to dexopt to enable the same restrictions during compilation.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1a5b86c..2e9a71a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -455,12 +455,6 @@
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
- if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
- || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- throw new IllegalArgumentException(
- "New installs into ASEC containers no longer supported");
- }
-
// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
@@ -487,21 +481,10 @@
if (!PackageHelper.fitsOnInternal(mContext, params)) {
throw new IOException("No suitable internal storage available");
}
-
- } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- if (!PackageHelper.fitsOnExternal(mContext, params)) {
- throw new IOException("No suitable external storage available");
- }
-
- } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
- // For now, installs to adopted media are treated as internal from
- // an install flag point-of-view.
- params.setInstallFlagsInternal();
-
} else {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
- params.setInstallFlagsInternal();
+ params.installFlags |= PackageManager.INSTALL_INTERNAL;
// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9856a2b..1045289 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -43,7 +43,6 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
-import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
@@ -54,7 +53,6 @@
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;
@@ -646,9 +644,6 @@
/** Directory where installed application's 32-bit native libraries are copied. */
private static final File sAppLib32InstallDir =
new File(Environment.getDataDirectory(), "app-lib");
- /** Directory where code and non-resource assets of forward-locked applications are stored */
- private static final File sDrmAppPrivateInstallDir =
- new File(Environment.getDataDirectory(), "app-private");
// ----------------------------------------------------------------
@@ -1811,12 +1806,10 @@
firstUserIds, firstInstantUserIds);
}
- // Send broadcast package appeared if forward locked/external for all users
- // treat asec-hosted packages like removable media on upgrade
- if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
+ // Send broadcast package appeared if external for all users
+ if (isExternal(res.pkg)) {
if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.pkg
- + " is ASEC-hosted -> AVAILABLE");
+ Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
}
final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
ArrayList<String> pkgList = new ArrayList<>(1);
@@ -2233,7 +2226,7 @@
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
String path = libConfig.valueAt(i);
- addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
+ addSharedLibraryLPw(path, null, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
}
// Builtin libraries cannot encode their dependency where they are
@@ -2629,10 +2622,6 @@
SystemClock.uptimeMillis());
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
- scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
- | PackageParser.PARSE_FORWARD_LOCK,
- scanFlags | SCAN_REQUIRE_KNOWN, 0);
-
// Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
@@ -4844,7 +4833,8 @@
}
SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(),
- libInfo.getPackageName(), libInfo.getName(), libInfo.getLongVersion(),
+ libInfo.getPackageName(), libInfo.getAllCodePaths(),
+ libInfo.getName(), libInfo.getLongVersion(),
libInfo.getType(), libInfo.getDeclaringPackage(),
getPackagesUsingSharedLibraryLPr(libInfo, flags, userId),
(libInfo.getDependencies() == null
@@ -5406,7 +5396,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) {
@@ -5425,7 +5415,7 @@
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- obj = mSettings.getUserIdLPr(uid2);
+ obj = mSettings.getSettingLPr(uid2);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
@@ -5484,7 +5474,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;
@@ -5597,7 +5587,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;
@@ -5633,7 +5623,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;
@@ -5661,7 +5651,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;
@@ -5710,7 +5700,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;
@@ -5732,7 +5722,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;
@@ -5755,7 +5745,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();
@@ -6395,7 +6385,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));
@@ -8630,7 +8620,7 @@
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
final InstallArgs args = createInstallArgsForExisting(
- packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+ pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
args.cleanUpResourcesLI();
synchronized (mPackages) {
@@ -8703,7 +8693,7 @@
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
InstallArgs args = createInstallArgsForExisting(
- packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+ pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
@@ -8725,7 +8715,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;
+ }
}
}
@@ -9225,7 +9221,7 @@
mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
}
}
- return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
+ return pdo.performDexOpt(p, p.usesLibraryInfos, instructionSets,
getOrCreateCompilerPackageStats(p),
mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
}
@@ -9628,7 +9624,6 @@
@GuardedBy("mPackages")
private void addSharedLibraryLPr(PackageParser.Package pkg, Set<String> usesLibraryFiles,
SharedLibraryInfo libInfo, PackageParser.Package changingLib) {
-
if (libInfo.getPath() != null) {
usesLibraryFiles.add(libInfo.getPath());
return;
@@ -10125,7 +10120,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);
}
}
}
@@ -10164,10 +10189,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);
}
@@ -11224,8 +11245,9 @@
}
}
- private boolean addSharedLibraryLPw(String path, String apk, String name, long version,
- int type, String declaringPackageName, long declaringVersionCode) {
+ private boolean addSharedLibraryLPw(String path, String apk, List<String> codePaths,
+ String name, long version, int type, String declaringPackageName,
+ long declaringVersionCode) {
LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
if (versionedLib == null) {
versionedLib = new LongSparseArray<>();
@@ -11236,7 +11258,7 @@
} else if (versionedLib.indexOfKey(version) >= 0) {
return false;
}
- SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, name,
+ SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, codePaths, name,
version, type, new VersionedPackage(declaringPackageName, declaringVersionCode),
null, null);
versionedLib.put(version, libraryInfo);
@@ -11323,10 +11345,17 @@
if (pkg.staticSharedLibName != null) {
// Static shared libs don't allow renaming as they have synthetic package
// names to allow install of multiple versions, so use name from manifest.
- if (addSharedLibraryLPw(null, pkg.packageName, pkg.staticSharedLibName,
+ if (addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(),
+ pkg.staticSharedLibName,
pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC,
pkg.manifestPackageName, pkg.getLongVersionCode())) {
hasStaticSharedLibs = true;
+ // Shared libraries for the package need to be updated.
+ try {
+ updateSharedLibrariesLPr(pkg, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
+ }
} else {
Slog.w(TAG, "Package " + pkg.packageName + " library "
+ pkg.staticSharedLibName + " already exists; skipping");
@@ -11368,13 +11397,19 @@
allowed = true;
}
if (allowed) {
- if (!addSharedLibraryLPw(null, pkg.packageName, name,
- SharedLibraryInfo.VERSION_UNDEFINED,
+ if (!addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(),
+ name, SharedLibraryInfo.VERSION_UNDEFINED,
SharedLibraryInfo.TYPE_DYNAMIC,
pkg.packageName, pkg.getLongVersionCode())) {
Slog.w(TAG, "Package " + pkg.packageName + " library "
+ name + " already exists; skipping");
}
+ // Shared libraries for the package need to be updated.
+ try {
+ updateSharedLibrariesLPr(pkg, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
+ }
} else {
Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
+ name + " that is not declared on system image; skipping");
@@ -11522,11 +11557,8 @@
// pass once we've determined ABI below.
setNativeLibraryPaths(pkg, sAppLib32InstallDir);
- // We would never need to extract libs for forward-locked and external packages,
- // since the container service will do it for us. We shouldn't attempt to
- // extract libs from system app when it was not updated.
- if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
- (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
+ // We shouldn't attempt to extract libs from system app when it was not updated.
+ if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
extractLibs = false;
}
@@ -11867,7 +11899,6 @@
final String codePath = pkg.codePath;
final File codeFile = new File(codePath);
final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
- final boolean asecApp = info.isForwardLocked() || info.isExternalAsec();
info.nativeLibraryRootDir = null;
info.nativeLibraryRootRequiresIsa = false;
@@ -11896,9 +11927,6 @@
info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
secondaryLibDir, apkName).getAbsolutePath();
}
- } else if (asecApp) {
- info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
- .getAbsolutePath();
} else {
final String apkName = deriveCodePathName(codePath);
info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
@@ -13482,7 +13510,7 @@
}
Signature[] callerSignature;
- Object obj = mSettings.getUserIdLPr(callingUid);
+ Object obj = mSettings.getSettingLPr(callingUid);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
callerSignature =
@@ -14007,7 +14035,6 @@
private int installLocationPolicy(PackageInfoLite pkgLite) {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
- boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
// reader
synchronized (mPackages) {
// Currently installed package which the new package is attempting to replace or
@@ -14060,16 +14087,8 @@
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// Check for updated system application.
if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- if (onSd) {
- Slog.w(TAG, "Cannot install update to system app on sdcard");
- return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;
- }
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
} else {
- if (onSd) {
- // Install flag overrides everything.
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
// If current upgrade specifies particular preference
if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
// Application explicitly specified internal.
@@ -14090,11 +14109,6 @@
}
}
}
- // All the special cases have been taken care of.
- // Return result based on recommended install location.
- if (onSd) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
return pkgLite.recommendedInstallLocation;
}
@@ -14111,70 +14125,59 @@
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
- installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
- final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
PackageInfoLite pkgLite = null;
- if (onInt && onSd) {
- // Check if both bits are set.
- Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
- ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
- } else if (onSd && ephemeral) {
- Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");
- ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
- } else {
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- origin.resolvedPath, installFlags, packageAbiOverride);
- if (DEBUG_INSTANT && ephemeral) {
- Slog.v(TAG, "pkgLite for install: " + pkgLite);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+ origin.resolvedPath, installFlags, packageAbiOverride);
+
+ if (DEBUG_INSTANT && ephemeral) {
+ Slog.v(TAG, "pkgLite for install: " + pkgLite);
+ }
+
+ /*
+ * If we have too little free space, try to free cache
+ * before giving up.
+ */
+ if (!origin.staged && pkgLite.recommendedInstallLocation
+ == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+ // TODO: focus freeing disk space on the target device
+ final StorageManager storage = StorageManager.from(mContext);
+ final long lowThreshold = storage.getStorageLowBytes(
+ Environment.getDataDirectory());
+
+ final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
+ origin.resolvedPath, packageAbiOverride);
+ if (sizeBytes >= 0) {
+ try {
+ mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+ origin.resolvedPath, installFlags, packageAbiOverride);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
+ }
}
/*
- * If we have too little free space, try to free cache
- * before giving up.
+ * The cache free must have deleted the file we downloaded to install.
+ *
+ * TODO: fix the "freeCache" call to not delete the file we care about.
*/
- if (!origin.staged && pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- // TODO: focus freeing disk space on the target device
- final StorageManager storage = StorageManager.from(mContext);
- final long lowThreshold = storage.getStorageLowBytes(
- Environment.getDataDirectory());
-
- final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
- origin.resolvedPath, packageAbiOverride);
- if (sizeBytes >= 0) {
- try {
- mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- origin.resolvedPath, installFlags, packageAbiOverride);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to free cache", e);
- }
- }
-
- /*
- * The cache free must have deleted the file we
- * downloaded to install.
- *
- * TODO: fix the "freeCache" call to not delete
- * the file we care about.
- */
- if (pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
- pkgLite.recommendedInstallLocation
+ if (pkgLite.recommendedInstallLocation
+ == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
+ pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
- }
}
}
+
if (ret == PackageManager.INSTALL_SUCCEEDED) {
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
@@ -14194,24 +14197,21 @@
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
- } else if (!onSd && !onInt) {
+ } else if (!onInt) {
// Override install location with flags
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
- installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
}
installFlags |= PackageManager.INSTALL_INSTANT_APP;
- installFlags &= ~(PackageManager.INSTALL_EXTERNAL
- |PackageManager.INSTALL_INTERNAL);
+ installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
// Make sure the flag for installing on external
// media is unset
installFlags |= PackageManager.INSTALL_INTERNAL;
- installFlags &= ~PackageManager.INSTALL_EXTERNAL;
}
}
}
@@ -14395,7 +14395,7 @@
* Create args that describe an existing installed package. Typically used
* when cleaning up old installs, or used as a move source.
*/
- private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
+ private InstallArgs createInstallArgsForExisting(String codePath,
String resourcePath, String[] instructionSets) {
return new FileInstallArgs(codePath, resourcePath, instructionSets);
}
@@ -14487,14 +14487,6 @@
return PackageManager.INSTALL_SUCCEEDED;
}
- protected boolean isFwdLocked() {
- return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
- }
-
- protected boolean isExternalAsec() {
- return (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
- }
-
protected boolean isEphemeral() {
return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
}
@@ -14522,7 +14514,7 @@
}
/**
- * Logic to handle installation of non-ASEC applications, including copying
+ * Logic to handle installation of new applications, including copying
* and renaming logic.
*/
class FileInstallArgs extends InstallArgs {
@@ -14544,9 +14536,6 @@
params.grantedRuntimePermissions,
params.traceMethod, params.traceCookie, params.signingDetails,
params.installReason, params.mParentInstallParams);
- if (isFwdLocked()) {
- throw new IllegalArgumentException("Forward locking only supported in ASEC");
- }
}
/** Existing install */
@@ -15201,8 +15190,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);
}
}
@@ -15221,10 +15213,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;
@@ -15233,6 +15227,7 @@
this.volumeUuid = volumeUuid;
this.prepareResult = prepareResult;
this.scanResult = scanResult;
+ this.deletePackageAction = deletePackageAction;
}
}
@@ -15245,14 +15240,29 @@
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;
+ if (prepareResult.replace) {
+ 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;
}
@@ -15277,7 +15287,7 @@
// We didn't need to disable the .apk as a current system package,
// which means we are replacing another update that is already
// installed. We need to make sure to delete the older one's .apk.
- res.removedInfo.args = createInstallArgsForExisting(0,
+ res.removedInfo.args = createInstallArgsForExisting(
oldPackage.applicationInfo.getCodePath(),
oldPackage.applicationInfo.getResourcePath(),
getAppDexInstructionSets(oldPackage.applicationInfo));
@@ -15324,29 +15334,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(
@@ -15400,8 +15410,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;
@@ -15556,8 +15568,6 @@
*/
private void executePostCommitSteps(CommitRequest commitRequest) {
for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
- final boolean forwardLocked =
- ((reconciledPkg.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
final boolean instantApp =
((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
@@ -15590,10 +15600,8 @@
// This update happens in place!
//
// We only need to dexopt if the package meets ALL of the following conditions:
- // 1) it is not forward locked.
- // 2) it is not on on an external ASEC container.
- // 3) it is not an instant app or if it is then dexopt is enabled via gservices.
- // 4) it is not debuggable.
+ // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
+ // 2) it is not debuggable.
//
// Note that we do not dexopt instant apps by default. dexopt can take some time to
// complete, so we skip this step during installation. Instead, we'll take extra time
@@ -15601,9 +15609,8 @@
// continuous progress to the useur instead of mysteriously blocking somewhere in the
// middle of running an instant app. The default behaviour can be overridden
// via gservices.
- final boolean performDexopt = !forwardLocked
- && !pkg.applicationInfo.isExternalAsec()
- && (!instantApp || Global.getInt(mContext.getContentResolver(),
+ final boolean performDexopt =
+ (!instantApp || Global.getInt(mContext.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
@@ -15617,7 +15624,7 @@
REASON_INSTALL,
DexoptOptions.DEXOPT_BOOT_COMPLETE
| DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
- mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
+ mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryInfos,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
mDexManager.getPackageUseInfoOrDefault(packageName),
@@ -15698,7 +15705,6 @@
// Parse old package
boolean oldExternal = isExternal(oldPackage);
int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
- | (oldPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
try {
@@ -15758,13 +15764,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;
@@ -15778,6 +15787,9 @@
this.system = system;
this.renamedPackage = renamedPackage;
this.freezer = freezer;
+ this.originalPs = originalPs;
+ this.disabledPs = disabledPs;
+ this.childPackageSettings = childPackageSettings;
}
}
@@ -15816,9 +15828,7 @@
final String installerPackageName = args.installerPackageName;
final String volumeUuid = args.volumeUuid;
final File tmpPackageFile = new File(args.getCodePath());
- final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
- final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
- || (args.volumeUuid != null));
+ final boolean onExternal = args.volumeUuid != null;
final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
@@ -15845,16 +15855,14 @@
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
// Sanity check
- if (instantApp && (forwardLocked || onExternal)) {
- Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
- + " external=" + onExternal);
+ if (instantApp && onExternal) {
+ Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
}
// Retrieve PackageSettings and parse package
@ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| PackageParser.PARSE_ENFORCE_CODE
- | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
| (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
@@ -16199,7 +16207,7 @@
pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
}
- } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
+ } else {
// Enable SCAN_NO_DEX flag to skip dexopt at a later stage
scanFlags |= SCAN_NO_DEX;
@@ -16285,7 +16293,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()) {
@@ -16305,7 +16315,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;
@@ -16332,6 +16341,7 @@
}
ps = mSettings.mPackages.get(pkgName11);
+ disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
// verify signatures are valid
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
@@ -16428,49 +16438,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
@@ -16513,6 +16526,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
@@ -16543,9 +16559,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();
@@ -16731,19 +16749,6 @@
return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
- private int packageFlagsToInstallFlags(PackageSetting ps) {
- int installFlags = 0;
- if (isExternal(ps) && TextUtils.isEmpty(ps.volumeUuid)) {
- // This existing package was an external ASEC install when we have
- // the external flag without a UUID
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- }
- if (ps.isForwardLocked()) {
- installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- }
- return installFlags;
- }
-
private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
if (isExternal(pkg)) {
if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -16759,9 +16764,6 @@
private void deleteTempPackageFiles() {
final FilenameFilter filter =
(dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
- for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
- file.delete();
- }
}
@Override
@@ -17511,34 +17513,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) {
@@ -17576,11 +17564,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) {
@@ -17597,25 +17582,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 =
@@ -17623,7 +17608,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)) {
@@ -17699,7 +17684,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) {
@@ -17714,9 +17699,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) {
@@ -17752,13 +17734,11 @@
// Delete application code and resources only for parent packages
if (ps.parentPackageName == null) {
if (deleteCodeAndResources && (outInfo != null)) {
- outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
+ outInfo.args = createInstallArgsForExisting(
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
}
}
-
- return true;
}
@Override
@@ -17814,26 +17794,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);
}
/*
@@ -17843,22 +17856,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) {
@@ -17874,7 +17908,7 @@
clearPackageStateForUserLIF(ps, removedUserId, outInfo);
markPackageUninstalledForUserLPw(ps, user);
scheduleWritePackageRestrictionsLocked(user);
- return true;
+ return;
}
}
@@ -17903,7 +17937,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.
@@ -17919,7 +17953,7 @@
if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
scheduleWritePackageRestrictionsLocked(user);
- return true;
+ return;
}
}
@@ -17944,15 +17978,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);
}
@@ -18001,8 +18035,6 @@
}
}
}
-
- return ret;
}
@GuardedBy("mPackages")
@@ -18463,7 +18495,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;
@@ -19728,7 +19760,7 @@
enableSystemPackageLPw(deletedPkg);
}
installPackageFromSystemLIF(deletedPkg.codePath,
- false /*isPrivileged*/, null /*allUserHandles*/,
+ /*isPrivileged*/ null /*allUserHandles*/,
null /*origUserHandles*/, null /*origPermissionsState*/,
true /*writeSettings*/);
} catch (PackageManagerException pme) {
@@ -21684,7 +21716,6 @@
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final PackageManager pm = mContext.getPackageManager();
- final boolean currentAsec;
final String currentVolumeUuid;
final File codeFile;
final String installerPackageName;
@@ -21718,22 +21749,13 @@
"3rd party apps are not allowed on internal storage");
}
- if (pkg.applicationInfo.isExternalAsec()) {
- currentAsec = true;
- currentVolumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
- } else if (pkg.applicationInfo.isForwardLocked()) {
- currentAsec = true;
- currentVolumeUuid = "forward_locked";
- } else {
- currentAsec = false;
- currentVolumeUuid = ps.volumeUuid;
+ currentVolumeUuid = ps.volumeUuid;
- final File probe = new File(pkg.codePath);
- final File probeOat = new File(probe, "oat");
- if (!probe.isDirectory() || !probeOat.isDirectory()) {
- throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
- "Move only supported for modern cluster style installs");
- }
+ final File probe = new File(pkg.codePath);
+ final File probeOat = new File(probe, "oat");
+ if (!probe.isDirectory() || !probeOat.isDirectory()) {
+ throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+ "Move only supported for modern cluster style installs");
}
if (Objects.equals(currentVolumeUuid, volumeUuid)) {
@@ -21770,12 +21792,11 @@
final boolean moveCompleteApp;
final File measurePath;
+ installFlags = INSTALL_INTERNAL;
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
- installFlags = INSTALL_INTERNAL;
- moveCompleteApp = !currentAsec;
+ moveCompleteApp = true;
measurePath = Environment.getDataAppDirectory(volumeUuid);
} else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
- installFlags = INSTALL_EXTERNAL;
moveCompleteApp = false;
measurePath = storage.getPrimaryPhysicalVolume().getPath();
} else {
@@ -21787,9 +21808,6 @@
"Move location not mounted private volume");
}
- Preconditions.checkState(!currentAsec);
-
- installFlags = INSTALL_INTERNAL;
moveCompleteApp = true;
measurePath = Environment.getDataAppDirectory(volumeUuid);
}
@@ -22574,7 +22592,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;
@@ -23576,6 +23594,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/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 31711e5..77f8c3a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2193,9 +2193,6 @@
boolean replaceExisting = true;
while ((opt = getNextOption()) != null) {
switch (opt) {
- case "-l":
- sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- break;
case "-r": // ignore
break;
case "-R":
@@ -2210,9 +2207,6 @@
case "-t":
sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
break;
- case "-s":
- sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
- break;
case "-f":
sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
break;
@@ -2821,7 +2815,8 @@
pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
- pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [PATH|-]");
+ pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [--apex]");
+ pw.println(" [PATH|-]");
pw.println(" Install an application. Must provide the apk data to install, either as a");
pw.println(" file path or '-' to read from stdin. Options are:");
pw.println(" -l: forward lock application");
@@ -2850,13 +2845,14 @@
pw.println(" --force-uuid: force install on to disk volume with given UUID");
pw.println(" --force-sdk: allow install even when existing app targets platform");
pw.println(" codename but new one targets a final API level");
+ pw.println(" --apex: install an .apex file, not an .apk");
pw.println("");
pw.println(" install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
- pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
+ pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]");
pw.println(" [--multi-package]");
pw.println(" Like \"install\", but starts an install session. Use \"install-write\"");
pw.println(" to push data into the session, and \"install-commit\" to finish.");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index b850613..2c2cc7e 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -152,10 +152,6 @@
return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
}
- public boolean isForwardLocked() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
- }
-
public boolean isSystem() {
return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 239dc99..fbf5439 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -64,7 +64,6 @@
| ApplicationInfo.PRIVATE_FLAG_VENDOR
| ApplicationInfo.PRIVATE_FLAG_PRODUCT
| ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES
- | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK
| ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6009bd3..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;
}
@@ -3656,7 +3658,6 @@
private static int PRE_M_APP_INFO_FLAG_HIDDEN = 1<<27;
private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
- private static int PRE_M_APP_INFO_FLAG_FORWARD_LOCK = 1<<29;
private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -3756,15 +3757,11 @@
if ((pkgFlags & PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE) != 0) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
}
- if ((pkgFlags & PRE_M_APP_INFO_FLAG_FORWARD_LOCK) != 0) {
- pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
- }
if ((pkgFlags & PRE_M_APP_INFO_FLAG_PRIVILEGED) != 0) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
pkgFlags &= ~(PRE_M_APP_INFO_FLAG_HIDDEN
| PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE
- | PRE_M_APP_INFO_FLAG_FORWARD_LOCK
| PRE_M_APP_INFO_FLAG_PRIVILEGED);
} else {
// For backward compatibility
@@ -4206,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() {
@@ -4258,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;
@@ -4428,7 +4462,6 @@
ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE, "DIRECT_BOOT_AWARE",
- ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS, "HAS_DOMAIN_URLS",
ApplicationInfo.PRIVATE_FLAG_HIDDEN, "HIDDEN",
ApplicationInfo.PRIVATE_FLAG_INSTANT, "EPHEMERAL",
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/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index 9a12a2f..93ee44c 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -17,6 +17,7 @@
package com.android.server.pm.dex;
import android.content.pm.ApplicationInfo;
+import android.content.pm.SharedLibraryInfo;
import android.util.Slog;
import android.util.SparseArray;
@@ -29,6 +30,11 @@
public final class DexoptUtils {
private static final String TAG = "DexoptUtils";
+ // Shared libraries have more or less followed PCL behavior due to the way
+ // they were added to the classpath pre Q.
+ private static final String SHARED_LIBRARY_LOADER_TYPE =
+ ClassLoaderFactory.getPathClassLoaderName();
+
private DexoptUtils() {}
/**
@@ -62,12 +68,15 @@
* android.app.ActivityThread, boolean, ApplicationInfo, List, List)}.
*/
public static String[] getClassLoaderContexts(ApplicationInfo info,
- String[] sharedLibraries, boolean[] pathsWithCode) {
+ List<SharedLibraryInfo> sharedLibraries, boolean[] pathsWithCode) {
// The base class loader context contains only the shared library.
- String sharedLibrariesClassPath = encodeClasspath(sharedLibraries);
- String baseApkContextClassLoader = encodeClassLoader(
- sharedLibrariesClassPath, info.classLoaderName);
+ String sharedLibrariesContext = "";
+ if (sharedLibraries != null) {
+ sharedLibrariesContext = encodeSharedLibraries(sharedLibraries);
+ }
+ String baseApkContextClassLoader = encodeClassLoader(
+ "", info.classLoaderName, sharedLibrariesContext);
if (info.getSplitCodePaths() == null) {
// The application has no splits.
return new String[] {baseApkContextClassLoader};
@@ -81,11 +90,10 @@
// The splits have an implicit dependency on the base apk.
// This means that we have to add the base apk file in addition to the shared libraries.
String baseApkName = new File(info.getBaseCodePath()).getName();
- String sharedLibrariesAndBaseClassPath =
- encodeClasspath(sharedLibrariesClassPath, baseApkName);
+ String baseClassPath = baseApkName;
// The result is stored in classLoaderContexts.
- // Index 0 is the class loaded context for the base apk.
+ // Index 0 is the class loader context for the base apk.
// Index `i` is the class loader context encoding for split `i`.
String[] classLoaderContexts = new String[/*base apk*/ 1 + splitRelativeCodePaths.length];
classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null;
@@ -94,10 +102,14 @@
// If the app didn't request for the splits to be loaded in isolation or if it does not
// declare inter-split dependencies, then all the splits will be loaded in the base
// apk class loader (in the order of their definition).
- String classpath = sharedLibrariesAndBaseClassPath;
+ String classpath = baseClassPath;
for (int i = 1; i < classLoaderContexts.length; i++) {
- classLoaderContexts[i] = pathsWithCode[i]
- ? encodeClassLoader(classpath, info.classLoaderName) : null;
+ if (pathsWithCode[i]) {
+ classLoaderContexts[i] = encodeClassLoader(
+ classpath, info.classLoaderName, sharedLibrariesContext);
+ } else {
+ classLoaderContexts[i] = null;
+ }
// Note that the splits with no code are not removed from the classpath computation.
// i.e. split_n might get the split_n-1 in its classpath dependency even
// if split_n-1 has no code.
@@ -124,7 +136,7 @@
info.splitClassLoaderNames[i]);
}
String splitDependencyOnBase = encodeClassLoader(
- sharedLibrariesAndBaseClassPath, info.classLoaderName);
+ baseClassPath, info.classLoaderName);
SparseArray<int[]> splitDependencies = info.splitDependencies;
// Note that not all splits have dependencies (e.g. configuration splits)
@@ -149,7 +161,8 @@
// any dependency. In this case its context equals its declared class loader.
classLoaderContexts[i] = classLoaderContexts[i] == null
? splitClassLoader
- : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i]);
+ : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i])
+ + sharedLibrariesContext;
} else {
// This is a split without code, it has no dependency and it is not compiled.
// Its context will be null.
@@ -207,6 +220,31 @@
return splitContext;
}
+ private static String encodeSharedLibrary(SharedLibraryInfo sharedLibrary) {
+ List<String> paths = sharedLibrary.getAllCodePaths();
+ String classLoaderSpec = encodeClassLoader(
+ encodeClasspath(paths.toArray(new String[paths.size()])),
+ SHARED_LIBRARY_LOADER_TYPE);
+ if (sharedLibrary.getDependencies() != null) {
+ classLoaderSpec += encodeSharedLibraries(sharedLibrary.getDependencies());
+ }
+ return classLoaderSpec;
+ }
+
+ private static String encodeSharedLibraries(List<SharedLibraryInfo> sharedLibraries) {
+ String sharedLibrariesContext = "{";
+ boolean first = true;
+ for (SharedLibraryInfo info : sharedLibraries) {
+ if (!first) {
+ sharedLibrariesContext += "#";
+ }
+ first = false;
+ sharedLibrariesContext += encodeSharedLibrary(info);
+ }
+ sharedLibrariesContext += "}";
+ return sharedLibrariesContext;
+ }
+
/**
* Encodes the shared libraries classpathElements in a format accepted by dexopt.
* NOTE: Keep this in sync with the dexopt expectations! Right now that is
@@ -258,6 +296,14 @@
}
/**
+ * Same as above, but appends {@param sharedLibraries} to the result.
+ */
+ private static String encodeClassLoader(String classpath, String classLoaderName,
+ String sharedLibraries) {
+ return encodeClassLoader(classpath, classLoaderName) + sharedLibraries;
+ }
+
+ /**
* Links to dependencies together in a format accepted by dexopt.
* For the special case when either of cl1 or cl2 equals
* {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns the same. This
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 774134c..21cc14e 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -195,7 +195,6 @@
// STOPSHIP(b/112545973): remove once feature enabled by default
if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
- MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_AUDIO);
}
}
@@ -203,10 +202,8 @@
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
- MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
- MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_IMAGES);
MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
- MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_VIDEO);
+ MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 59c6d0a..3ba1155 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -20,13 +20,7 @@
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_TOAST_WINDOW;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
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.content.Context.CONTEXT_RESTRICTED;
import static android.content.Context.DISPLAY_SERVICE;
import static android.content.Context.WINDOW_SERVICE;
@@ -35,61 +29,28 @@
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.EMPTY;
-import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
-import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.O;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.STATE_OFF;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
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.FLAG_TRANSLUCENT_NAVIGATION;
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_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.MATCH_PARENT;
-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;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -106,19 +67,13 @@
import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
@@ -132,25 +87,15 @@
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
-import static com.android.server.wm.WindowManagerPolicyProto.FOCUSED_APP_TOKEN;
-import static com.android.server.wm.WindowManagerPolicyProto.FOCUSED_WINDOW;
-import static com.android.server.wm.WindowManagerPolicyProto.FORCE_STATUS_BAR;
-import static com.android.server.wm.WindowManagerPolicyProto.FORCE_STATUS_BAR_FROM_KEYGUARD;
import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DRAW_COMPLETE;
import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED;
import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_CHANGED;
import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_PENDING;
-import static com.android.server.wm.WindowManagerPolicyProto.LAST_SYSTEM_UI_FLAGS;
-import static com.android.server.wm.WindowManagerPolicyProto.NAVIGATION_BAR;
import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION;
-import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION_LISTENER;
import static com.android.server.wm.WindowManagerPolicyProto.ROTATION;
import static com.android.server.wm.WindowManagerPolicyProto.ROTATION_MODE;
import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY;
-import static com.android.server.wm.WindowManagerPolicyProto.STATUS_BAR;
-import static com.android.server.wm.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW;
-import static com.android.server.wm.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_WINDOW;
import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;
import android.annotation.Nullable;
@@ -158,12 +103,10 @@
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
-import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.IUiModeManager;
import android.app.ProgressDialog;
import android.app.SearchManager;
-import android.app.StatusBarManager;
import android.app.UiModeManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
@@ -181,15 +124,12 @@
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
-import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
-import android.hardware.power.V1_0.PowerHint;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
@@ -202,7 +142,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IDeviceIdleController;
-import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
@@ -224,7 +163,6 @@
import android.service.vr.IPersistentVrStateCallbacks;
import android.speech.RecognizerIntent;
import android.telecom.TelecomManager;
-import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
import android.util.LongSparseArray;
@@ -234,21 +172,13 @@
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
-import android.view.DisplayCutout;
-import android.view.Gravity;
import android.view.HapticFeedbackConstants;
-import android.view.IApplicationToken;
import android.view.IWindowManager;
-import android.view.InputChannel;
import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
import android.view.KeyCharacterMap;
import android.view.KeyCharacterMap.FallbackAction;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.PointerIcon;
-import android.view.Surface;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
@@ -263,8 +193,6 @@
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -272,7 +200,6 @@
import com.android.internal.policy.PhoneWindow;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.widget.PointerLocationView;
import com.android.server.ExtconStateObserver;
@@ -289,19 +216,17 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.AppTransition;
-import com.android.server.wm.DisplayFrames;
import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
-import com.android.server.wm.WindowFrames;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-import com.android.server.wm.utils.InsetUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.HashSet;
import java.util.List;
/**
@@ -313,11 +238,9 @@
*/
public class PhoneWindowManager implements WindowManagerPolicy {
static final String TAG = "WindowManager";
- static final boolean DEBUG = false;
static final boolean localLOGV = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_KEYGUARD = false;
- static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_SPLASH_SCREEN = false;
static final boolean DEBUG_WAKEUP = false;
static final boolean SHOW_SPLASH_SCREENS = true;
@@ -329,8 +252,6 @@
// Whether to allow devices placed in vr headset viewers to have an alternative Home intent.
static final boolean ENABLE_VR_HEADSET_HOME_CAPTURE = true;
- static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false;
-
static final int SHORT_PRESS_POWER_NOTHING = 0;
static final int SHORT_PRESS_POWER_GO_TO_SLEEP = 1;
static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2;
@@ -372,13 +293,6 @@
static final int PENDING_KEY_NULL = -1;
- // Controls navigation bar opacity depending on which workspace stacks are currently
- // visible.
- // Nav bar is always opaque when either the freeform stack or docked stack is visible.
- static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0;
- // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque.
- static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 1;
-
static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
@@ -386,33 +300,11 @@
static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
- /**
- * These are the system UI flags that, when changing, can cause the layout
- * of the screen to change.
- */
- static final int SYSTEM_UI_CHANGING_LAYOUT =
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.STATUS_BAR_TRANSLUCENT
- | View.NAVIGATION_BAR_TRANSLUCENT
- | View.STATUS_BAR_TRANSPARENT
- | View.NAVIGATION_BAR_TRANSPARENT;
-
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build();
- // The panic gesture may become active only after the keyguard is dismissed and the immersive
- // app shows again. If that doesn't happen for 30s we drop the gesture.
- private static final long PANIC_GESTURE_EXPIRATION = 30000;
-
- private static final String SYSUI_PACKAGE = "com.android.systemui";
- private static final String SYSUI_SCREENSHOT_SERVICE =
- "com.android.systemui.screenshot.TakeScreenshotService";
- private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
- "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
-
/**
* Keyguard stuff
*/
@@ -501,16 +393,7 @@
private AccessibilityShortcutController mAccessibilityShortcutController;
boolean mSafeMode;
- private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>();
- WindowState mStatusBar = null;
- private final int[] mStatusBarHeightForRotation = new int[4];
- WindowState mNavigationBar = null;
- @NavigationBarPosition
- int mNavigationBarPosition = NAV_BAR_BOTTOM;
- int[] mNavigationBarHeightForRotationDefault = new int[4];
- int[] mNavigationBarWidthForRotationDefault = new int[4];
- int[] mNavigationBarHeightForRotationInCarMode = new int[4];
- int[] mNavigationBarWidthForRotationInCarMode = new int[4];
+ private WindowState mKeyguardCandidate = null;
private LongSparseArray<IShortcutService> mShortcutKeyServices = new LongSparseArray<>();
@@ -591,7 +474,6 @@
int mShortPressOnSleepBehavior;
int mShortPressOnWindowBehavior;
boolean mHasSoftInput = false;
- boolean mTranslucentDecorEnabled = true;
boolean mUseTvRouting;
int mVeryLongPressTimeout;
boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
@@ -600,74 +482,12 @@
private boolean mHandleVolumeKeysInWM;
int mPointerLocationMode = 0; // guarded by mLock
-
- // The windows we were told about in focusChanged.
- WindowState mFocusedWindow;
- WindowState mLastFocusedWindow;
-
- IApplicationToken mFocusedApp;
-
PointerLocationView mPointerLocationView;
- // During layout, the layer at which the doc window is placed.
- int mDockLayer;
- // During layout, this is the layer of the status bar.
- int mStatusBarLayer;
- int mLastSystemUiFlags;
- // Bits that we are in the process of clearing, so we want to prevent
- // them from being set by applications until everything has been updated
- // to have them clear.
- int mResettingSystemUiFlags = 0;
- // Bits that we are currently always keeping cleared.
- int mForceClearedSystemUiFlags = 0;
- int mLastFullscreenStackSysUiFlags;
- int mLastDockedStackSysUiFlags;
- final Rect mNonDockedStackBounds = new Rect();
- final Rect mDockedStackBounds = new Rect();
- final Rect mLastNonDockedStackBounds = new Rect();
- final Rect mLastDockedStackBounds = new Rect();
-
- // What we last reported to system UI about whether the compatibility
- // menu needs to be displayed.
- boolean mLastFocusNeedsMenu = false;
- // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
- private long mPendingPanicGestureUptime;
-
- InputConsumer mInputConsumer = null;
-
- private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
- private static final Rect sTmpRect = new Rect();
- private static final Rect sTmpDockedFrame = new Rect();
- private static final Rect sTmpNavFrame = new Rect();
- private static final Rect sTmpLastParentFrame = new Rect();
-
- WindowState mTopFullscreenOpaqueWindowState;
- WindowState mTopFullscreenOpaqueOrDimmingWindowState;
- WindowState mTopDockedOpaqueWindowState;
- WindowState mTopDockedOpaqueOrDimmingWindowState;
- boolean mTopIsFullscreen;
- boolean mForceStatusBar;
- boolean mForceStatusBarFromKeyguard;
- private boolean mForceStatusBarTransparent;
- int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
- boolean mForcingShowNavBar;
- int mForcingShowNavBarLayer;
-
private boolean mPendingKeyguardOccluded;
private boolean mKeyguardOccludedChanged;
private boolean mNotifyUserActivity;
- boolean mShowingDream;
- private boolean mLastShowingDream;
- boolean mDreamingLockscreen;
- boolean mDreamingSleepTokenNeeded;
- private boolean mWindowSleepTokenNeeded;
- private boolean mLastWindowSleepTokenNeeded;
-
- @GuardedBy("mHandler")
- private SleepToken mWindowSleepToken;
-
- SleepToken mDreamingSleepToken;
SleepToken mScreenOffSleepToken;
volatile boolean mKeyguardOccluded;
Intent mHomeIntent;
@@ -680,10 +500,9 @@
boolean mPendingCapsLockToggle;
int mMetaState;
int mInitialMetaState;
- boolean mForceShowSystemBars;
// support for activating the lock screen while the screen is on
- boolean mAllowLockscreenWhenOn;
+ private HashSet<Integer> mAllowLockscreenWhenOnDisplays = new HashSet<>();
int mLockScreenTimeout;
boolean mLockScreenTimerActive;
@@ -704,7 +523,6 @@
Display mDefaultDisplay;
DisplayRotation mDefaultDisplayRotation;
DisplayPolicy mDefaultDisplayPolicy;
- WindowOrientationListener mDefaultOrientationListener;
// What we do when the user long presses on home
private int mLongPressOnHomeBehavior;
@@ -781,10 +599,6 @@
private boolean mAodShowing;
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- private NavigationBarExperiments mExperiments = new NavigationBarExperiments();
- // EXPERIMENT END
-
private static final int MSG_ENABLE_POINTER_LOCATION = 1;
private static final int MSG_DISABLE_POINTER_LOCATION = 2;
private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
@@ -798,25 +612,19 @@
private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12;
private static final int MSG_POWER_DELAYED_PRESS = 13;
private static final int MSG_POWER_LONG_PRESS = 14;
- private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 15;
- private static final int MSG_REQUEST_TRANSIENT_BARS = 16;
- private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 17;
- private static final int MSG_BACK_LONG_PRESS = 18;
- private static final int MSG_DISPOSE_INPUT_CONSUMER = 19;
- private static final int MSG_ACCESSIBILITY_SHORTCUT = 20;
- private static final int MSG_BUGREPORT_TV = 21;
- private static final int MSG_ACCESSIBILITY_TV = 22;
- private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 23;
- private static final int MSG_SYSTEM_KEY_PRESS = 24;
- private static final int MSG_HANDLE_ALL_APPS = 25;
- private static final int MSG_LAUNCH_ASSIST = 26;
- private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 27;
- private static final int MSG_POWER_VERY_LONG_PRESS = 28;
- private static final int MSG_NOTIFY_USER_ACTIVITY = 29;
- private static final int MSG_RINGER_TOGGLE_CHORD = 30;
-
- private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
- private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
+ private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15;
+ private static final int MSG_BACK_LONG_PRESS = 16;
+ private static final int MSG_ACCESSIBILITY_SHORTCUT = 17;
+ private static final int MSG_BUGREPORT_TV = 18;
+ private static final int MSG_ACCESSIBILITY_TV = 19;
+ private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 20;
+ private static final int MSG_SYSTEM_KEY_PRESS = 21;
+ private static final int MSG_HANDLE_ALL_APPS = 22;
+ private static final int MSG_LAUNCH_ASSIST = 23;
+ private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 24;
+ private static final int MSG_POWER_VERY_LONG_PRESS = 25;
+ private static final int MSG_NOTIFY_USER_ACTIVITY = 26;
+ private static final int MSG_RINGER_TOGGLE_CHORD = 27;
private class PolicyHandler extends Handler {
@Override
@@ -876,25 +684,12 @@
case MSG_POWER_VERY_LONG_PRESS:
powerVeryLongPress();
break;
- case MSG_UPDATE_DREAMING_SLEEP_TOKEN:
- updateDreamingSleepToken(msg.arg1 != 0);
- break;
- case MSG_REQUEST_TRANSIENT_BARS:
- WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS) ?
- mStatusBar : mNavigationBar;
- if (targetBar != null) {
- requestTransientBars(targetBar);
- }
- break;
case MSG_SHOW_PICTURE_IN_PICTURE_MENU:
showPictureInPictureMenuInternal();
break;
case MSG_BACK_LONG_PRESS:
backLongPress();
break;
- case MSG_DISPOSE_INPUT_CONSUMER:
- disposeInputConsumer((InputConsumer) msg.obj);
- break;
case MSG_ACCESSIBILITY_SHORTCUT:
accessibilityShortcutActivated();
break;
@@ -966,14 +761,8 @@
Settings.Secure.DEFAULT_INPUT_METHOD), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS), false, this,
- UserHandle.USER_ALL);
- resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.VOLUME_HUSH_GESTURE), false, this,
UserHandle.USER_ALL);
- resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.POLICY_CONTROL), false, this,
- UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED), false, this,
UserHandle.USER_ALL);
@@ -1012,45 +801,6 @@
}
};
- private final StatusBarController mStatusBarController = new StatusBarController();
-
- private final BarController mNavigationBarController = new BarController("NavigationBar",
- View.NAVIGATION_BAR_TRANSIENT,
- View.NAVIGATION_BAR_UNHIDE,
- View.NAVIGATION_BAR_TRANSLUCENT,
- StatusBarManager.WINDOW_NAVIGATION_BAR,
- FLAG_TRANSLUCENT_NAVIGATION,
- View.NAVIGATION_BAR_TRANSPARENT);
-
- private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
- new BarController.OnBarVisibilityChangedListener() {
- @Override
- public void onBarVisibilityChanged(boolean visible) {
- mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible);
- }
- };
-
- private final Runnable mAcquireSleepTokenRunnable = () -> {
- if (mWindowSleepToken != null) {
- return;
- }
- mWindowSleepToken = mActivityTaskManagerInternal.acquireSleepToken("WindowSleepToken",
- DEFAULT_DISPLAY);
- };
-
- private final Runnable mReleaseSleepTokenRunnable = () -> {
- if (mWindowSleepToken == null) {
- return;
- }
- mWindowSleepToken.release();
- mWindowSleepToken = null;
- };
-
- private ImmersiveModeConfirmation mImmersiveModeConfirmation;
-
- @VisibleForTesting
- SystemGesturesPointerEventListener mSystemGestures;
-
private void handleRingerChordGesture() {
if (mRingerToggleChord == VOLUME_HUSH_OFF) {
return;
@@ -1150,14 +900,7 @@
mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);
}
- // Detect user pressing the power button in panic when an application has
- // taken over the whole screen.
- boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
- SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags),
- isNavBarEmpty(mLastSystemUiFlags));
- if (panic) {
- mHandler.post(mHiddenNavPanic);
- }
+ mWindowManagerFuncs.onPowerKeyDown(interactive);
// Abort possibly stuck animations.
mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
@@ -1500,12 +1243,6 @@
mAccessibilityShortcutController.performAccessibilityShortcut();
}
- private void disposeInputConsumer(InputConsumer inputConsumer) {
- if (inputConsumer != null) {
- inputConsumer.dismiss();
- }
- }
-
private void sleepPress() {
if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
launchHomeFromHotKey(DEFAULT_DISPLAY, false /* awakenDreams */,
@@ -1643,9 +1380,7 @@
@Override
public void run() {
- mScreenshotHelper.takeScreenshot(mScreenshotType,
- mStatusBar != null && mStatusBar.isVisibleLw(),
- mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler);
+ mDefaultDisplayPolicy.takeScreenshot(mScreenshotType);
}
}
@@ -1673,7 +1408,8 @@
mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0;
}
- boolean isUserSetupComplete() {
+ @Override
+ public boolean isUserSetupComplete() {
boolean isSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
if (mHasFeatureLeanback) {
@@ -1931,7 +1667,6 @@
mDefaultDisplay = displayContentInfo.getDisplay();
mDefaultDisplayRotation = displayContentInfo.getDisplayRotation();
mDefaultDisplayPolicy = mDefaultDisplayRotation.getDisplayPolicy();
- mDefaultOrientationListener = mDefaultDisplayRotation.getOrientationListener();
}
/** {@inheritDoc} */
@@ -2028,8 +1763,6 @@
com.android.internal.R.bool.config_lidControlsScreenLock);
mLidControlsSleep = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_lidControlsSleep);
- mTranslucentDecorEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableTranslucentDecor);
mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
@@ -2107,76 +1840,6 @@
filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
context.registerReceiver(mMultiuserReceiver, filter);
- // monitor for system gestures
- // TODO(multi-display): Needs to be display specific.
- mSystemGestures = new SystemGesturesPointerEventListener(context,
- new SystemGesturesPointerEventListener.Callbacks() {
- @Override
- public void onSwipeFromTop() {
- if (mStatusBar != null) {
- requestTransientBars(mStatusBar);
- }
- }
- @Override
- public void onSwipeFromBottom() {
- if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) {
- requestTransientBars(mNavigationBar);
- }
- }
- @Override
- public void onSwipeFromRight() {
- if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) {
- requestTransientBars(mNavigationBar);
- }
- }
- @Override
- public void onSwipeFromLeft() {
- if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) {
- requestTransientBars(mNavigationBar);
- }
- }
- @Override
- public void onFling(int duration) {
- if (mPowerManagerInternal != null) {
- mPowerManagerInternal.powerHint(
- PowerHint.INTERACTION, duration);
- }
- }
- @Override
- public void onDebug() {
- // no-op
- }
- @Override
- public void onDown() {
- mDefaultOrientationListener.onTouchStart();
- }
- @Override
- public void onUpOrCancel() {
- mDefaultOrientationListener.onTouchEnd();
- }
- @Override
- public void onMouseHoverAtTop() {
- mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
- Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
- msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS;
- mHandler.sendMessageDelayed(msg, 500);
- }
- @Override
- public void onMouseHoverAtBottom() {
- mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
- Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
- msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION;
- mHandler.sendMessageDelayed(msg, 500);
- }
- @Override
- public void onMouseLeaveFromEdge() {
- mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
- }
- });
- mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext);
- //TODO (b/111365687) : make system context per display.
- mWindowManagerFuncs.registerPointerEventListener(mSystemGestures, DEFAULT_DISPLAY);
-
mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
mLongPressVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_longPressVibePattern);
@@ -2199,8 +1862,6 @@
finishedGoingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
}
- mWindowManagerInternal.registerAppTransitionListener(
- mStatusBarController.getAppTransitionListener());
mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
@Override
public int onAppTransitionStartingLocked(int transit, IBinder openToken,
@@ -2255,16 +1916,6 @@
if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
}
-
- mNavBarOpacityMode = res.getInteger(
- com.android.internal.R.integer.config_navBarOpacityMode);
- }
-
- /**
- * @return whether the navigation bar can be hidden, e.g. the device has a navigation bar
- */
- private boolean canHideNavigationBar() {
- return mDefaultDisplayPolicy.hasNavigationBar();
}
public void updateSettings() {
@@ -2322,12 +1973,6 @@
mHasSoftInput = hasSoftInput;
updateRotation = true;
}
- if (mImmersiveModeConfirmation != null) {
- mImmersiveModeConfirmation.loadSetting(mCurrentUserId);
- }
- }
- synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
- PolicyControl.reloadFromSetting(mContext);
}
if (updateRotation) {
updateRotation(true);
@@ -2536,84 +2181,6 @@
return mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW) != PERMISSION_GRANTED;
}
- @Override
- public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
- boolean hasStatusBarServicePermission) {
-
- final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
- if (mScreenDecorWindows.contains(win)) {
- if (!isScreenDecor) {
- // No longer has the flag set, so remove from the set.
- mScreenDecorWindows.remove(win);
- }
- } else if (isScreenDecor && hasStatusBarServicePermission) {
- mScreenDecorWindows.add(win);
- }
-
- switch (attrs.type) {
- case TYPE_SYSTEM_OVERLAY:
- case TYPE_SECURE_SYSTEM_OVERLAY:
- // These types of windows can't receive input events.
- attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
- break;
- case TYPE_DREAM:
- case TYPE_WALLPAPER:
- // Dreams and wallpapers don't have an app window token and can thus not be
- // letterboxed. Hence always let them extend under the cutout.
- attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- break;
- case TYPE_STATUS_BAR:
-
- // If the Keyguard is in a hidden state (occluded by another window), we force to
- // remove the wallpaper and keyguard flag so that any change in-flight after setting
- // the keyguard as occluded wouldn't set these flags again.
- // See {@link #processKeyguardSetHiddenResultLw}.
- if (mKeyguardOccluded) {
- attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
- attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
- }
- break;
-
- case TYPE_SCREENSHOT:
- attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- break;
-
- case TYPE_TOAST:
- // While apps should use the dedicated toast APIs to add such windows
- // it possible legacy apps to add the window directly. Therefore, we
- // make windows added directly by the app behave as a toast as much
- // as possible in terms of timeout and animation.
- if (attrs.hideTimeoutMilliseconds < 0
- || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) {
- attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
- }
- attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
- break;
- }
-
- if (attrs.type != TYPE_STATUS_BAR) {
- // The status bar is the only window allowed to exhibit keyguard behavior.
- attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
- }
- }
-
- private int getImpliedSysUiFlagsForLayout(LayoutParams attrs) {
- int impliedFlags = 0;
- if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
- impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- }
- final boolean forceWindowDrawsStatusBarBackground =
- (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
- if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
- || forceWindowDrawsStatusBarBackground
- && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) {
- impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- }
- return impliedFlags;
- }
-
void readLidState() {
mDefaultDisplayPolicy.setLidState(mWindowManagerFuncs.getLidState());
}
@@ -2660,161 +2227,10 @@
}
@Override
- public void onOverlayChangedLw(DisplayContentInfo displayContentInfo) {
- onConfigurationChanged(displayContentInfo);
- }
-
- @Override
- public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
- final DisplayRotation displayRotation = displayContentInfo.getDisplayRotation();
- // TODO(multi-display): Define policy for secondary displays.
- if (!displayRotation.isDefaultDisplay) {
- return;
- }
-
- final Context uiContext = getSystemUiContext();
- final Resources res = uiContext.getResources();
- final int portraitRotation = displayRotation.getPortraitRotation();
- final int upsideDownRotation = displayRotation.getUpsideDownRotation();
- final int landscapeRotation = displayRotation.getLandscapeRotation();
- final int seascapeRotation = displayRotation.getSeascapeRotation();
-
- mStatusBarHeightForRotation[portraitRotation] =
- mStatusBarHeightForRotation[upsideDownRotation] = res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height_portrait);
- mStatusBarHeightForRotation[landscapeRotation] =
- mStatusBarHeightForRotation[seascapeRotation] = res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height_landscape);
-
- // Height of the navigation bar when presented horizontally at bottom
- mNavigationBarHeightForRotationDefault[portraitRotation] =
- mNavigationBarHeightForRotationDefault[upsideDownRotation] =
- res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
- mNavigationBarHeightForRotationDefault[landscapeRotation] =
- mNavigationBarHeightForRotationDefault[seascapeRotation] = res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height_landscape);
-
- // Width of the navigation bar when presented vertically along one side
- mNavigationBarWidthForRotationDefault[portraitRotation] =
- mNavigationBarWidthForRotationDefault[upsideDownRotation] =
- mNavigationBarWidthForRotationDefault[landscapeRotation] =
- mNavigationBarWidthForRotationDefault[seascapeRotation] =
- res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
-
- if (ALTERNATE_CAR_MODE_NAV_SIZE) {
- // Height of the navigation bar when presented horizontally at bottom
- mNavigationBarHeightForRotationInCarMode[portraitRotation] =
- mNavigationBarHeightForRotationInCarMode[upsideDownRotation] =
- res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height_car_mode);
- mNavigationBarHeightForRotationInCarMode[landscapeRotation] =
- mNavigationBarHeightForRotationInCarMode[seascapeRotation] = res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
-
- // Width of the navigation bar when presented vertically along one side
- mNavigationBarWidthForRotationInCarMode[portraitRotation] =
- mNavigationBarWidthForRotationInCarMode[upsideDownRotation] =
- mNavigationBarWidthForRotationInCarMode[landscapeRotation] =
- mNavigationBarWidthForRotationInCarMode[seascapeRotation] =
- res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_width_car_mode);
- }
-
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- mExperiments.onConfigurationChanged(uiContext);
- // EXPERIMENT END
- }
-
- @VisibleForTesting
- Context getSystemUiContext() {
- return ActivityThread.currentActivityThread().getSystemUiContext();
- }
-
- @Override
public int getMaxWallpaperLayer() {
return getWindowLayerFromTypeLw(TYPE_STATUS_BAR);
}
- private int getNavigationBarWidth(int rotation, int uiMode) {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarWidthForRotationInCarMode[rotation];
- } else {
- return mNavigationBarWidthForRotationDefault[rotation];
- }
- }
-
- @Override
- public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
- int displayId, DisplayCutout displayCutout) {
- int width = fullWidth;
- // TODO(multi-display): Support navigation bar on secondary displays.
- if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) {
- // For a basic navigation bar, when we are in landscape mode we place
- // the navigation bar to the side.
- if (mDefaultDisplayPolicy.navigationBarCanMove() && fullWidth > fullHeight) {
- width -= getNavigationBarWidth(rotation, uiMode);
- }
- }
- if (displayCutout != null) {
- width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
- }
- return width;
- }
-
- private int getNavigationBarHeight(int rotation, int uiMode) {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarHeightForRotationInCarMode[rotation];
- } else {
- return mNavigationBarHeightForRotationDefault[rotation];
- }
- }
-
- @Override
- public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
- int displayId, DisplayCutout displayCutout) {
- int height = fullHeight;
- // TODO(multi-display): Support navigation bar on secondary displays.
- if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) {
- // For a basic navigation bar, when we are in portrait mode we place
- // the navigation bar to the bottom.
- if (!mDefaultDisplayPolicy.navigationBarCanMove() || fullWidth < fullHeight) {
- height -= getNavigationBarHeight(rotation, uiMode);
- }
- }
- if (displayCutout != null) {
- height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();
- }
- return height;
- }
-
- @Override
- public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
- int displayId, DisplayCutout displayCutout) {
- return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayId,
- displayCutout);
- }
-
- @Override
- public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
- int displayId, DisplayCutout displayCutout) {
- // There is a separate status bar at the top of the display. We don't count that as part
- // of the fixed decor, since it can hide; however, for purposes of configurations,
- // we do want to exclude it since applications can't generally use that part
- // of the screen.
- // TODO(multi-display): Support status bars on secondary displays.
- if (displayId == DEFAULT_DISPLAY) {
- int statusBarHeight = mStatusBarHeightForRotation[rotation];
- if (displayCutout != null) {
- // If there is a cutout, it may already have accounted for some part of the status
- // bar height.
- statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
- }
- return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayId,
- displayCutout) - statusBarHeight;
- }
- return fullHeight;
- }
-
@Override
public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
return attrs.type == TYPE_STATUS_BAR;
@@ -3054,251 +2470,6 @@
return context.createDisplayContext(targetDisplay);
}
- /**
- * Preflight adding a window to the system.
- *
- * Currently enforces that three window types are singletons:
- * <ul>
- * <li>STATUS_BAR_TYPE</li>
- * <li>KEYGUARD_TYPE</li>
- * </ul>
- *
- * @param win The window to be added
- * @param attrs Information about the window to be added
- *
- * @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons,
- * WindowManagerImpl.ADD_MULTIPLE_SINGLETON
- */
- @Override
- public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) {
-
- if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.STATUS_BAR_SERVICE,
- "PhoneWindowManager");
- mScreenDecorWindows.add(win);
- }
-
- switch (attrs.type) {
- case TYPE_STATUS_BAR:
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.STATUS_BAR_SERVICE,
- "PhoneWindowManager");
- if (mStatusBar != null) {
- if (mStatusBar.isAlive()) {
- return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
- }
- }
- mStatusBar = win;
- mStatusBarController.setWindow(win);
- setKeyguardOccludedLw(mKeyguardOccluded, true /* force */);
- break;
- case TYPE_NAVIGATION_BAR:
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.STATUS_BAR_SERVICE,
- "PhoneWindowManager");
- if (mNavigationBar != null) {
- if (mNavigationBar.isAlive()) {
- return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
- }
- }
- mNavigationBar = win;
- mNavigationBarController.setWindow(win);
- mNavigationBarController.setOnBarVisibilityChangedListener(
- mNavBarVisibilityListener, true);
- if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
- break;
- case TYPE_NAVIGATION_BAR_PANEL:
- case TYPE_STATUS_BAR_PANEL:
- case TYPE_STATUS_BAR_SUB_PANEL:
- case TYPE_VOICE_INTERACTION_STARTING:
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.STATUS_BAR_SERVICE,
- "PhoneWindowManager");
- break;
- }
- return ADD_OKAY;
- }
-
- /** {@inheritDoc} */
- @Override
- public void removeWindowLw(WindowState win) {
- if (mStatusBar == win) {
- mStatusBar = null;
- mStatusBarController.setWindow(null);
- } else if (mNavigationBar == win) {
- mNavigationBar = null;
- mNavigationBarController.setWindow(null);
- }
- if (mLastFocusedWindow == win) {
- mLastFocusedWindow = null;
- }
- mScreenDecorWindows.remove(win);
- }
-
- static final boolean PRINT_ANIM = false;
-
- /** {@inheritDoc} */
- @Override
- public int selectAnimationLw(WindowState win, int transit) {
- if (PRINT_ANIM) Log.i(TAG, "selectAnimation in " + win
- + ": transit=" + transit);
- if (win == mStatusBar) {
- final boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
- final boolean expanded = win.getAttrs().height == MATCH_PARENT
- && win.getAttrs().width == MATCH_PARENT;
- if (isKeyguard || expanded) {
- return -1;
- }
- if (transit == TRANSIT_EXIT
- || transit == TRANSIT_HIDE) {
- return R.anim.dock_top_exit;
- } else if (transit == TRANSIT_ENTER
- || transit == TRANSIT_SHOW) {
- return R.anim.dock_top_enter;
- }
- } else if (win == mNavigationBar) {
- if (win.getAttrs().windowAnimations != 0) {
- return 0;
- }
- // This can be on either the bottom or the right or the left.
- if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
- if (transit == TRANSIT_EXIT
- || transit == TRANSIT_HIDE) {
- if (isKeyguardShowingAndNotOccluded()) {
- return R.anim.dock_bottom_exit_keyguard;
- } else {
- return R.anim.dock_bottom_exit;
- }
- } else if (transit == TRANSIT_ENTER
- || transit == TRANSIT_SHOW) {
- return R.anim.dock_bottom_enter;
- }
- } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
- if (transit == TRANSIT_EXIT
- || transit == TRANSIT_HIDE) {
- return R.anim.dock_right_exit;
- } else if (transit == TRANSIT_ENTER
- || transit == TRANSIT_SHOW) {
- return R.anim.dock_right_enter;
- }
- } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
- if (transit == TRANSIT_EXIT
- || transit == TRANSIT_HIDE) {
- return R.anim.dock_left_exit;
- } else if (transit == TRANSIT_ENTER
- || transit == TRANSIT_SHOW) {
- return R.anim.dock_left_enter;
- }
- }
- } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
- return selectDockedDividerAnimationLw(win, transit);
- }
-
- if (transit == TRANSIT_PREVIEW_DONE) {
- if (win.hasAppShownWindows()) {
- if (PRINT_ANIM) Log.i(TAG, "**** STARTING EXIT");
- return com.android.internal.R.anim.app_starting_exit;
- }
- } else if (win.getAttrs().type == TYPE_DREAM && mDreamingLockscreen
- && transit == TRANSIT_ENTER) {
- // Special case: we are animating in a dream, while the keyguard
- // is shown. We don't want an animation on the dream, because
- // we need it shown immediately with the keyguard animating away
- // to reveal it.
- return -1;
- }
-
- return 0;
- }
-
- private int selectDockedDividerAnimationLw(WindowState win, int transit) {
- int insets = mWindowManagerFuncs.getDockedDividerInsetsLw();
-
- // If the divider is behind the navigation bar, don't animate.
- final Rect frame = win.getFrameLw();
- final boolean behindNavBar = mNavigationBar != null
- && ((mNavigationBarPosition == NAV_BAR_BOTTOM
- && frame.top + insets >= mNavigationBar.getFrameLw().top)
- || (mNavigationBarPosition == NAV_BAR_RIGHT
- && frame.left + insets >= mNavigationBar.getFrameLw().left)
- || (mNavigationBarPosition == NAV_BAR_LEFT
- && frame.right - insets <= mNavigationBar.getFrameLw().right));
- final boolean landscape = frame.height() > frame.width();
- final boolean offscreenLandscape = landscape && (frame.right - insets <= 0
- || frame.left + insets >= win.getDisplayFrameLw().right);
- final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0
- || frame.bottom + insets >= win.getDisplayFrameLw().bottom);
- final boolean offscreen = offscreenLandscape || offscreenPortrait;
- if (behindNavBar || offscreen) {
- return 0;
- }
- if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
- return R.anim.fade_in;
- } else if (transit == TRANSIT_EXIT) {
- return R.anim.fade_out;
- } else {
- return 0;
- }
- }
-
- @Override
- public void selectRotationAnimationLw(int anim[]) {
- // If the screen is off or non-interactive, force a jumpcut.
- final boolean forceJumpcut = !mDefaultDisplayPolicy.isScreenOnFully() || !okToAnimate();
- if (PRINT_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen="
- + mTopFullscreenOpaqueWindowState + " rotationAnimation="
- + (mTopFullscreenOpaqueWindowState == null ?
- "0" : mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation)
- + " forceJumpcut=" + forceJumpcut);
- if (forceJumpcut) {
- anim[0] = R.anim.rotation_animation_jump_exit;
- anim[1] = R.anim.rotation_animation_enter;
- return;
- }
- if (mTopFullscreenOpaqueWindowState != null) {
- int animationHint = mTopFullscreenOpaqueWindowState.getRotationAnimationHint();
- if (animationHint < 0 && mTopIsFullscreen) {
- animationHint = mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation;
- }
- switch (animationHint) {
- case ROTATION_ANIMATION_CROSSFADE:
- case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless.
- anim[0] = R.anim.rotation_animation_xfade_exit;
- anim[1] = R.anim.rotation_animation_enter;
- break;
- case ROTATION_ANIMATION_JUMPCUT:
- anim[0] = R.anim.rotation_animation_jump_exit;
- anim[1] = R.anim.rotation_animation_enter;
- break;
- case ROTATION_ANIMATION_ROTATE:
- default:
- anim[0] = anim[1] = 0;
- break;
- }
- } else {
- anim[0] = anim[1] = 0;
- }
- }
-
- @Override
- public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId,
- boolean forceDefault) {
- switch (exitAnimId) {
- case R.anim.rotation_animation_xfade_exit:
- case R.anim.rotation_animation_jump_exit:
- // These are the only cases that matter.
- if (forceDefault) {
- return false;
- }
- int anim[] = new int[2];
- selectRotationAnimationLw(anim);
- return (exitAnimId == anim[0] && enterAnimId == anim[1]);
- default:
- return true;
- }
- }
-
@Override
public Animation createHiddenByKeyguardExit(boolean onWallpaper,
boolean goingToNotificationShade) {
@@ -4173,77 +3344,6 @@
}
}
- private final Runnable mClearHideNavigationFlag = new Runnable() {
- @Override
- public void run() {
- synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
- // Clear flags.
- mForceClearedSystemUiFlags &=
- ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
- }
- mWindowManagerFuncs.reevaluateStatusBarVisibility();
- }
- };
-
- /**
- * Input handler used while nav bar is hidden. Captures any touch on the screen,
- * to determine when the nav bar should be shown and prevent applications from
- * receiving those touches.
- */
- final class HideNavInputEventReceiver extends InputEventReceiver {
- public HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
-
- @Override
- public void onInputEvent(InputEvent event) {
- boolean handled = false;
- try {
- if (event instanceof MotionEvent
- && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- final MotionEvent motionEvent = (MotionEvent)event;
- if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
- // When the user taps down, we re-show the nav bar.
- boolean changed = false;
- synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
- if (mInputConsumer == null) {
- return;
- }
- // Any user activity always causes us to show the
- // navigation controls, if they had been hidden.
- // We also clear the low profile and only content
- // flags so that tapping on the screen will atomically
- // restore all currently hidden screen decorations.
- int newVal = mResettingSystemUiFlags |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_LOW_PROFILE |
- View.SYSTEM_UI_FLAG_FULLSCREEN;
- if (mResettingSystemUiFlags != newVal) {
- mResettingSystemUiFlags = newVal;
- changed = true;
- }
- // We don't allow the system's nav bar to be hidden
- // again for 1 second, to prevent applications from
- // spamming us and keeping it from being shown.
- newVal = mForceClearedSystemUiFlags |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
- if (mForceClearedSystemUiFlags != newVal) {
- mForceClearedSystemUiFlags = newVal;
- changed = true;
- mHandler.postDelayed(mClearHideNavigationFlag, 1000);
- }
- }
- if (changed) {
- mWindowManagerFuncs.reevaluateStatusBarVisibility();
- }
- }
- }
- } finally {
- finishInputEvent(event, handled);
- }
- }
- }
-
@Override
public void setRecentsVisibilityLw(boolean visible) {
mRecentsVisible = visible;
@@ -4259,1177 +3359,9 @@
mNavBarVirtualKeyHapticFeedbackEnabled = enabled;
}
- @Override
- public int adjustSystemUiVisibilityLw(int visibility) {
- mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
- mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
-
- // Reset any bits in mForceClearingStatusBarVisibility that
- // are now clear.
- mResettingSystemUiFlags &= visibility;
- // Clear any bits in the new visibility that are currently being
- // force cleared, before reporting it.
- return visibility & ~mResettingSystemUiFlags
- & ~mForceClearedSystemUiFlags;
- }
-
- @Override
- // TODO: Should probably be moved into DisplayFrames.
- public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds,
- DisplayFrames displayFrames, boolean floatingStack, Rect outFrame,
- Rect outContentInsets, Rect outStableInsets,
- Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
- final int fl = PolicyControl.getWindowFlags(null, attrs);
- final int pfl = attrs.privateFlags;
- final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
- final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs);
- final int displayRotation = displayFrames.mRotation;
-
- final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
- if (useOutsets) {
- int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
- if (outset > 0) {
- if (displayRotation == Surface.ROTATION_0) {
- outOutsets.bottom += outset;
- } else if (displayRotation == Surface.ROTATION_90) {
- outOutsets.right += outset;
- } else if (displayRotation == Surface.ROTATION_180) {
- outOutsets.top += outset;
- } else if (displayRotation == Surface.ROTATION_270) {
- outOutsets.left += outset;
- }
- }
- }
-
- final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0;
- final boolean layoutInScreenAndInsetDecor = layoutInScreen &&
- (fl & FLAG_LAYOUT_INSET_DECOR) != 0;
- final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
-
- if (layoutInScreenAndInsetDecor && !screenDecor) {
- if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
- outFrame.set(displayFrames.mUnrestricted);
- } else {
- outFrame.set(displayFrames.mRestricted);
- }
-
- final Rect sf;
- if (floatingStack) {
- sf = null;
- } else {
- sf = displayFrames.mStable;
- }
-
- final Rect cf;
- if (floatingStack) {
- cf = null;
- } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
- if ((fl & FLAG_FULLSCREEN) != 0) {
- cf = displayFrames.mStableFullscreen;
- } else {
- cf = displayFrames.mStable;
- }
- } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
- cf = displayFrames.mOverscan;
- } else {
- cf = displayFrames.mCurrent;
- }
-
- if (taskBounds != null) {
- outFrame.intersect(taskBounds);
- }
- InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets);
- InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets);
- outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame)
- .getDisplayCutout());
- return mForceShowSystemBars;
- } else {
- if (layoutInScreen) {
- outFrame.set(displayFrames.mUnrestricted);
- } else {
- outFrame.set(displayFrames.mStable);
- }
- if (taskBounds != null) {
- outFrame.intersect(taskBounds);
- }
-
- outContentInsets.setEmpty();
- outStableInsets.setEmpty();
- outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
- return mForceShowSystemBars;
- }
- }
-
- private boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) {
- return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0;
- }
-
/** {@inheritDoc} */
@Override
- public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
- displayFrames.onBeginLayout();
- // TODO(multi-display): This doesn't seem right...Maybe only apply to default display?
- mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
- mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
- mDockLayer = 0x10000000;
- mStatusBarLayer = -1;
-
- if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
- // For purposes of putting out fake window up to steal focus, we will
- // drive nav being hidden only by whether it is requested.
- final int sysui = mLastSystemUiFlags;
- boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
- boolean navTranslucent = (sysui
- & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
- boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
- boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
- boolean navAllowedHidden = immersive || immersiveSticky;
- navTranslucent &= !immersiveSticky; // transient trumps translucent
- boolean isKeyguardShowing = isStatusBarKeyguard() && !mKeyguardOccluded;
- if (!isKeyguardShowing) {
- navTranslucent &= areTranslucentBarsAllowed();
- }
- boolean statusBarForcesShowingNavigation = !isKeyguardShowing && mStatusBar != null
- && (mStatusBar.getAttrs().privateFlags
- & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
-
- // When the navigation bar isn't visible, we put up a fake input window to catch all
- // touch events. This way we can detect when the user presses anywhere to bring back the
- // nav bar and ensure the application doesn't see the event.
- if (navVisible || navAllowedHidden) {
- if (mInputConsumer != null) {
- mHandler.sendMessage(
- mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
- mInputConsumer = null;
- }
- } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
- mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(),
- INPUT_CONSUMER_NAVIGATION,
- (channel, looper) -> new HideNavInputEventReceiver(channel, looper),
- displayFrames.mDisplayId);
- // As long as mInputConsumer is active, hover events are not dispatched to the app
- // and the pointer icon is likely to become stale. Hide it to avoid confusion.
- InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
- }
-
- // For purposes of positioning and showing the nav bar, if we have decided that it can't
- // be hidden (because of the screen aspect ratio), then take that into account.
- navVisible |= !canHideNavigationBar();
-
- boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
- navTranslucent, navAllowedHidden, statusBarForcesShowingNavigation);
- if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
- updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing);
- if (updateSysUiVisibility) {
- updateSystemUiVisibilityLw();
- }
- }
- layoutScreenDecorWindows(displayFrames);
-
- if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
- // Make sure that the zone we're avoiding for the cutout is at least as tall as the
- // status bar; otherwise fullscreen apps will end up cutting halfway into the status
- // bar.
- displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,
- displayFrames.mStable.top);
- }
- }
-
- private void layoutScreenDecorWindows(DisplayFrames displayFrames) {
- if (mScreenDecorWindows.isEmpty()) {
- return;
- }
-
- sTmpRect.setEmpty();
- sTmpDockedFrame.set(displayFrames.mDock);
-
- final int displayId = displayFrames.mDisplayId;
- final Rect dockFrame = displayFrames.mDock;
- final int displayHeight = displayFrames.mDisplayHeight;
- final int displayWidth = displayFrames.mDisplayWidth;
-
- for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) {
- final WindowState w = mScreenDecorWindows.valueAt(i);
- if (w.getDisplayId() != displayId || !w.isVisibleLw()) {
- // Skip if not on the same display or not visible.
- continue;
- }
-
- w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */,
- sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */,
- sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */,
- sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */,
- sTmpDockedFrame /* outsetFrame */);
- w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
- w.computeFrameLw();
- final Rect frame = w.getFrameLw();
-
- if (frame.left <= 0 && frame.top <= 0) {
- // Docked at left or top.
- if (frame.bottom >= displayHeight) {
- // Docked left.
- dockFrame.left = Math.max(frame.right, dockFrame.left);
- } else if (frame.right >= displayWidth ) {
- // Docked top.
- dockFrame.top = Math.max(frame.bottom, dockFrame.top);
- } else {
- Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
- + " not docked on left or top of display. frame=" + frame
- + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
- }
- } else if (frame.right >= displayWidth && frame.bottom >= displayHeight) {
- // Docked at right or bottom.
- if (frame.top <= 0) {
- // Docked right.
- dockFrame.right = Math.min(frame.left, dockFrame.right);
- } else if (frame.left <= 0) {
- // Docked bottom.
- dockFrame.bottom = Math.min(frame.top, dockFrame.bottom);
- } else {
- Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
- + " not docked on right or bottom" + " of display. frame=" + frame
- + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
- }
- } else {
- // Screen decor windows are required to be docked on one of the sides of the screen.
- Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
- + " not docked on one of the sides of the display. frame=" + frame
- + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
- }
- }
-
- displayFrames.mRestricted.set(dockFrame);
- displayFrames.mCurrent.set(dockFrame);
- displayFrames.mVoiceContent.set(dockFrame);
- displayFrames.mSystem.set(dockFrame);
- displayFrames.mContent.set(dockFrame);
- displayFrames.mRestrictedOverscan.set(dockFrame);
- }
-
- private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
- boolean isKeyguardShowing) {
- // decide where the status bar goes ahead of time
- if (mStatusBar == null) {
- return false;
- }
- // apply any navigation bar insets
- sTmpRect.setEmpty();
- mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
- displayFrames.mUnrestricted /* displayFrame */,
- displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
- displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
- displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
- mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
- mStatusBarLayer = mStatusBar.getSurfaceLayer();
-
- // Let the status bar determine its size.
- mStatusBar.computeFrameLw();
-
- // For layout, the status bar is always at the top with our fixed height.
- displayFrames.mStable.top = displayFrames.mUnrestricted.top
- + mStatusBarHeightForRotation[displayFrames.mRotation];
- // Make sure the status bar covers the entire cutout height
- displayFrames.mStable.top = Math.max(displayFrames.mStable.top,
- displayFrames.mDisplayCutoutSafe.top);
-
- // Tell the bar controller where the collapsed status bar content is
- sTmpRect.set(mStatusBar.getContentFrameLw());
- sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
- sTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset
- sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
- mStatusBarController.setContentFrame(sTmpRect);
-
- boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
- boolean statusBarTranslucent = (sysui
- & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
- if (!isKeyguardShowing) {
- statusBarTranslucent &= areTranslucentBarsAllowed();
- }
-
- // If the status bar is hidden, we don't want to cause windows behind it to scroll.
- if (mStatusBar.isVisibleLw() && !statusBarTransient) {
- // Status bar may go away, so the screen area it occupies is available to apps but just
- // covering them when the status bar is visible.
- final Rect dockFrame = displayFrames.mDock;
- dockFrame.top = displayFrames.mStable.top;
- displayFrames.mContent.set(dockFrame);
- displayFrames.mVoiceContent.set(dockFrame);
- displayFrames.mCurrent.set(dockFrame);
-
- if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
- "dock=%s content=%s cur=%s", dockFrame.toString(),
- displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
-
- if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
- && !mStatusBarController.wasRecentlyTranslucent()) {
- // If the opaque status bar is currently requested to be visible, and not in the
- // process of animating on or off, then we can tell the app that it is covered by it.
- displayFrames.mSystem.top = displayFrames.mStable.top;
- }
- }
- return mStatusBarController.checkHiddenLw();
- }
-
- private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
- boolean navTranslucent, boolean navAllowedHidden,
- boolean statusBarForcesShowingNavigation) {
- if (mNavigationBar == null) {
- return false;
- }
-
- final Rect navigationFrame = sTmpNavFrame;
- boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
- // Force the navigation bar to its appropriate place and size. We need to do this directly,
- // instead of relying on it to bubble up from the nav bar, because this needs to change
- // atomically with screen rotations.
- final int rotation = displayFrames.mRotation;
- final int displayHeight = displayFrames.mDisplayHeight;
- final int displayWidth = displayFrames.mDisplayWidth;
- final Rect dockFrame = displayFrames.mDock;
- mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
-
- final Rect cutoutSafeUnrestricted = sTmpRect;
- cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
- cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
-
- if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
- // It's a system nav bar or a portrait screen; nav bar goes on bottom.
- final int top = cutoutSafeUnrestricted.bottom
- - getNavigationBarHeight(rotation, uiMode);
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- final int topNavBar = cutoutSafeUnrestricted.bottom
- - mExperiments.getNavigationBarFrameHeight();
- navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom);
- // EXPERIMENT END
- displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- dockFrame.bottom = displayFrames.mRestricted.bottom
- = displayFrames.mRestrictedOverscan.bottom = top;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status bar.
- mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the opaque nav bar is currently requested to be visible and not in the process
- // of animating on or off, then we can tell the app that it is covered by it.
- displayFrames.mSystem.bottom = top;
- }
- } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
- // Landscape screen; nav bar goes to the right.
- final int left = cutoutSafeUnrestricted.right
- - getNavigationBarWidth(rotation, uiMode);
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- final int leftNavBar = cutoutSafeUnrestricted.right
- - mExperiments.getNavigationBarFrameWidth();
- navigationFrame.set(leftNavBar, 0, displayFrames.mUnrestricted.right, displayHeight);
- // EXPERIMENT END
- displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- dockFrame.right = displayFrames.mRestricted.right
- = displayFrames.mRestrictedOverscan.right = left;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status bar.
- mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the nav bar is currently requested to be visible, and not in the process of
- // animating on or off, then we can tell the app that it is covered by it.
- displayFrames.mSystem.right = left;
- }
- } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
- // Seascape screen; nav bar goes to the left.
- final int right = cutoutSafeUnrestricted.left
- + getNavigationBarWidth(rotation, uiMode);
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- final int rightNavBar = cutoutSafeUnrestricted.left
- + mExperiments.getNavigationBarFrameWidth();
- navigationFrame.set(displayFrames.mUnrestricted.left, 0, rightNavBar, displayHeight);
- // EXPERIMENT END
- displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- dockFrame.left = displayFrames.mRestricted.left =
- displayFrames.mRestrictedOverscan.left = right;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status bar.
- mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the nav bar is currently requested to be visible, and not in the process of
- // animating on or off, then we can tell the app that it is covered by it.
- displayFrames.mSystem.left = right;
- }
- }
-
- // Make sure the content and current rectangles are updated to account for the restrictions
- // from the navigation bar.
- displayFrames.mCurrent.set(dockFrame);
- displayFrames.mVoiceContent.set(dockFrame);
- displayFrames.mContent.set(dockFrame);
- mStatusBarLayer = mNavigationBar.getSurfaceLayer();
- // And compute the final frame.
- sTmpRect.setEmpty();
- mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */,
- navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
- displayFrames.mDisplayCutoutSafe /* contentFrame */,
- navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
- navigationFrame /* stableFrame */,
- displayFrames.mDisplayCutoutSafe /* outsetFrame */);
- mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
- mNavigationBar.computeFrameLw();
- mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
-
- if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
- return mNavigationBarController.checkHiddenLw();
- }
-
- @NavigationBarPosition
- private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
- if (mDefaultDisplayPolicy.navigationBarCanMove() && displayWidth > displayHeight) {
- if (displayRotation == Surface.ROTATION_270) {
- return NAV_BAR_LEFT;
- } else {
- return NAV_BAR_RIGHT;
- }
- }
- return NAV_BAR_BOTTOM;
- }
-
- /** {@inheritDoc} */
- @Override
- public int getSystemDecorLayerLw() {
- if (mStatusBar != null && mStatusBar.isVisibleLw()) {
- return mStatusBar.getSurfaceLayer();
- }
-
- if (mNavigationBar != null && mNavigationBar.isVisibleLw()) {
- return mNavigationBar.getSurfaceLayer();
- }
-
- return 0;
- }
-
- private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
- boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf,
- DisplayFrames displayFrames) {
- if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) {
- // Here's a special case: if the child window is not the 'dock window'
- // or input method target, and the window it is attached to is below
- // the dock window, then the frames we computed for the window it is
- // attached to can not be used because the dock is effectively part
- // of the underlying window and the attached window is floating on top
- // of the whole thing. So, we ignore the attached window and explicitly
- // compute the frames that would be appropriate without the dock.
- vf.set(displayFrames.mDock);
- cf.set(displayFrames.mDock);
- of.set(displayFrames.mDock);
- df.set(displayFrames.mDock);
- } else {
- // The effective display frame of the attached window depends on whether it is taking
- // care of insetting its content. If not, we need to use the parent's content frame so
- // that the entire window is positioned within that content. Otherwise we can use the
- // overscan frame and let the attached window take care of positioning its content
- // appropriately.
- if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- // Set the content frame of the attached window to the parent's decor frame
- // (same as content frame when IME isn't present) if specifically requested by
- // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag.
- // Otherwise, use the overscan frame.
- cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0
- ? attached.getContentFrameLw() : attached.getOverscanFrameLw());
- } else {
- // If the window is resizing, then we want to base the content frame on our attached
- // content frame to resize...however, things can be tricky if the attached window is
- // NOT in resize mode, in which case its content frame will be larger.
- // Ungh. So to deal with that, make sure the content frame we end up using is not
- // covering the IM dock.
- cf.set(attached.getContentFrameLw());
- if (attached.isVoiceInteraction()) {
- cf.intersectUnchecked(displayFrames.mVoiceContent);
- } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) {
- cf.intersectUnchecked(displayFrames.mContent);
- }
- }
- df.set(insetDecors ? attached.getDisplayFrameLw() : cf);
- of.set(insetDecors ? attached.getOverscanFrameLw() : cf);
- vf.set(attached.getVisibleFrameLw());
- }
- // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be
- // positioned relative to its parent or the entire screen.
- pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df);
- }
-
- private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) {
- if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) {
- return;
- }
- // If app is requesting a stable layout, don't let the content insets go below the stable
- // values.
- if ((fl & FLAG_FULLSCREEN) != 0) {
- r.intersectUnchecked(displayFrames.mStableFullscreen);
- } else {
- r.intersectUnchecked(displayFrames.mStable);
- }
- }
-
- private boolean canReceiveInput(WindowState win) {
- boolean notFocusable =
- (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0;
- boolean altFocusableIm =
- (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0;
- boolean notFocusableForIm = notFocusable ^ altFocusableIm;
- return !notFocusableForIm;
- }
-
- /** {@inheritDoc} */
- @Override
- public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
- // We've already done the navigation bar, status bar, and all screen decor windows. If the
- // status bar can receive input, we need to layout it again to accommodate for the IME
- // window.
- if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar
- || mScreenDecorWindows.contains(win)) {
- return;
- }
- final WindowManager.LayoutParams attrs = win.getAttrs();
- final boolean isDefaultDisplay = win.isDefaultDisplay();
-
- final int type = attrs.type;
- final int fl = PolicyControl.getWindowFlags(win, attrs);
- final int pfl = attrs.privateFlags;
- final int sim = attrs.softInputMode;
- final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
- final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
-
- final WindowFrames windowFrames = win.getWindowFrames();
-
- windowFrames.setHasOutsets(false);
- sTmpLastParentFrame.set(windowFrames.mParentFrame);
- final Rect pf = windowFrames.mParentFrame;
- final Rect df = windowFrames.mDisplayFrame;
- final Rect of = windowFrames.mOverscanFrame;
- final Rect cf = windowFrames.mContentFrame;
- final Rect vf = windowFrames.mVisibleFrame;
- final Rect dcf = windowFrames.mDecorFrame;
- final Rect sf = windowFrames.mStableFrame;
- dcf.setEmpty();
- windowFrames.setParentFrameWasClippedByDisplayCutout(false);
- windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
-
- final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar()
- && mNavigationBar != null && mNavigationBar.isVisibleLw());
-
- final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
-
- final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
- || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
-
- final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
- final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
-
- sf.set(displayFrames.mStable);
-
- if (type == TYPE_INPUT_METHOD) {
- vf.set(displayFrames.mDock);
- cf.set(displayFrames.mDock);
- of.set(displayFrames.mDock);
- df.set(displayFrames.mDock);
- windowFrames.mParentFrame.set(displayFrames.mDock);
- // IM dock windows layout below the nav bar...
- pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
- // ...with content insets above the nav bar
- cf.bottom = vf.bottom = displayFrames.mStable.bottom;
- // TODO (b/111364446): Support showing IME on non-default displays
- if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
- // The status bar forces the navigation bar while it's visible. Make sure the IME
- // avoids the navigation bar in that case.
- if (mNavigationBarPosition == NAV_BAR_RIGHT) {
- pf.right = df.right = of.right = cf.right = vf.right =
- displayFrames.mStable.right;
- } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
- pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
- }
- }
-
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- // Offset the ime to avoid overlapping with the nav bar
- mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win);
- // EXPERIMENT END
-
- // IM dock windows always go to the bottom of the screen.
- attrs.gravity = Gravity.BOTTOM;
- mDockLayer = win.getSurfaceLayer();
- } else if (type == TYPE_VOICE_INTERACTION) {
- of.set(displayFrames.mUnrestricted);
- df.set(displayFrames.mUnrestricted);
- pf.set(displayFrames.mUnrestricted);
- if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- cf.set(displayFrames.mDock);
- } else {
- cf.set(displayFrames.mContent);
- }
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.set(displayFrames.mCurrent);
- } else {
- vf.set(cf);
- }
- } else if (type == TYPE_WALLPAPER) {
- layoutWallpaper(displayFrames, pf, df, of, cf);
- } else if (win == mStatusBar) {
- of.set(displayFrames.mUnrestricted);
- df.set(displayFrames.mUnrestricted);
- pf.set(displayFrames.mUnrestricted);
- cf.set(displayFrames.mStable);
- vf.set(displayFrames.mStable);
-
- if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
- cf.bottom = displayFrames.mContent.bottom;
- } else {
- cf.bottom = displayFrames.mDock.bottom;
- vf.bottom = displayFrames.mContent.bottom;
- }
- } else {
- dcf.set(displayFrames.mSystem);
- final boolean inheritTranslucentDecor =
- (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
- final boolean isAppWindow =
- type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
- final boolean topAtRest =
- win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
- if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
- if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
- && (fl & FLAG_FULLSCREEN) == 0
- && (fl & FLAG_TRANSLUCENT_STATUS) == 0
- && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
- && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) {
- // Ensure policy decor includes status bar
- dcf.top = displayFrames.mStable.top;
- }
- if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
- && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
- && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
- // Ensure policy decor includes navigation bar
- dcf.bottom = displayFrames.mStable.bottom;
- dcf.right = displayFrames.mStable.right;
- }
- }
-
- if (layoutInScreen && layoutInsetDecor) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
- + "): IN_SCREEN, INSET_DECOR");
- // This is the case for a normal activity window: we want it to cover all of the
- // screen space, and it can take care of moving its contents to account for screen
- // decorations that intrude into that space.
- if (attached != null) {
- // If this window is attached to another, our display
- // frame is the same as the one we are attached to.
- setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf,
- displayFrames);
- } else {
- if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
- // Status bar panels are the only windows who can go on top of the status
- // bar. They are protected by the STATUS_BAR_SERVICE permission, so they
- // have the same privileges as the status bar itself.
- //
- // However, they should still dodge the navigation bar if it exists.
-
- pf.left = df.left = of.left = hasNavBar
- ? displayFrames.mDock.left : displayFrames.mUnrestricted.left;
- pf.top = df.top = of.top = displayFrames.mUnrestricted.top;
- pf.right = df.right = of.right = hasNavBar
- ? displayFrames.mRestricted.right
- : displayFrames.mUnrestricted.right;
- pf.bottom = df.bottom = of.bottom = hasNavBar
- ? displayFrames.mRestricted.bottom
- : displayFrames.mUnrestricted.bottom;
-
- if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf);
- } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
- && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
- // Asking to layout into the overscan region, so give it that pure
- // unrestricted area.
- of.set(displayFrames.mOverscan);
- df.set(displayFrames.mOverscan);
- pf.set(displayFrames.mOverscan);
- } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
- && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW
- || type == TYPE_VOLUME_OVERLAY)) {
- // Asking for layout as if the nav bar is hidden, lets the application
- // extend into the unrestricted overscan screen area. We only do this for
- // application windows and certain system windows to ensure no window that
- // can be above the nav bar can do this.
- df.set(displayFrames.mOverscan);
- pf.set(displayFrames.mOverscan);
- // We need to tell the app about where the frame inside the overscan is, so
- // it can inset its content by that amount -- it didn't ask to actually
- // extend itself into the overscan region.
- of.set(displayFrames.mUnrestricted);
- } else {
- df.set(displayFrames.mRestrictedOverscan);
- pf.set(displayFrames.mRestrictedOverscan);
- // We need to tell the app about where the frame inside the overscan
- // is, so it can inset its content by that amount -- it didn't ask
- // to actually extend itself into the overscan region.
- of.set(displayFrames.mUnrestricted);
- }
-
- if ((fl & FLAG_FULLSCREEN) == 0) {
- if (win.isVoiceInteraction()) {
- cf.set(displayFrames.mVoiceContent);
- } else {
- if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- cf.set(displayFrames.mDock);
- } else {
- cf.set(displayFrames.mContent);
- }
- }
- } else {
- // Full screen windows are always given a layout that is as if the status
- // bar and other transient decors are gone. This is to avoid bad states when
- // moving from a window that is not hiding the status bar to one that is.
- cf.set(displayFrames.mRestricted);
- }
- applyStableConstraints(sysUiFl, fl, cf, displayFrames);
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.set(displayFrames.mCurrent);
- } else {
- vf.set(cf);
- }
-
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win);
- // EXPERIMENT END
- }
- } else if (layoutInScreen || (sysUiFl
- & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
- + "): IN_SCREEN");
- // A window that has requested to fill the entire screen just
- // gets everything, period.
- if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
- cf.set(displayFrames.mUnrestricted);
- of.set(displayFrames.mUnrestricted);
- df.set(displayFrames.mUnrestricted);
- pf.set(displayFrames.mUnrestricted);
- if (hasNavBar) {
- pf.left = df.left = of.left = cf.left = displayFrames.mDock.left;
- pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right;
- pf.bottom = df.bottom = of.bottom = cf.bottom =
- displayFrames.mRestricted.bottom;
- }
- if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf);
- } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
- // The navigation bar has Real Ultimate Power.
- of.set(displayFrames.mUnrestricted);
- df.set(displayFrames.mUnrestricted);
- pf.set(displayFrames.mUnrestricted);
- if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf);
- } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
- && ((fl & FLAG_FULLSCREEN) != 0)) {
- // Fullscreen secure system overlays get what they ask for. Screenshot region
- // selection overlay should also expand to full screen.
- cf.set(displayFrames.mOverscan);
- of.set(displayFrames.mOverscan);
- df.set(displayFrames.mOverscan);
- pf.set(displayFrames.mOverscan);
- } else if (type == TYPE_BOOT_PROGRESS) {
- // Boot progress screen always covers entire display.
- cf.set(displayFrames.mOverscan);
- of.set(displayFrames.mOverscan);
- df.set(displayFrames.mOverscan);
- pf.set(displayFrames.mOverscan);
- } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
- && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
- // Asking to layout into the overscan region, so give it that pure unrestricted
- // area.
- cf.set(displayFrames.mOverscan);
- of.set(displayFrames.mOverscan);
- df.set(displayFrames.mOverscan);
- pf.set(displayFrames.mOverscan);
- } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
- && (type == TYPE_STATUS_BAR
- || type == TYPE_TOAST
- || type == TYPE_DOCK_DIVIDER
- || type == TYPE_VOICE_INTERACTION_STARTING
- || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) {
- // Asking for layout as if the nav bar is hidden, lets the
- // application extend into the unrestricted screen area. We
- // only do this for application windows (or toasts) to ensure no window that
- // can be above the nav bar can do this.
- // XXX This assumes that an app asking for this will also
- // ask for layout in only content. We can't currently figure out
- // what the screen would be if only laying out to hide the nav bar.
- cf.set(displayFrames.mUnrestricted);
- of.set(displayFrames.mUnrestricted);
- df.set(displayFrames.mUnrestricted);
- pf.set(displayFrames.mUnrestricted);
- } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
- of.set(displayFrames.mRestricted);
- df.set(displayFrames.mRestricted);
- pf.set(displayFrames.mRestricted);
- if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- cf.set(displayFrames.mDock);
- } else {
- cf.set(displayFrames.mContent);
- }
- } else {
- cf.set(displayFrames.mRestricted);
- of.set(displayFrames.mRestricted);
- df.set(displayFrames.mRestricted);
- pf.set(displayFrames.mRestricted);
- }
-
- applyStableConstraints(sysUiFl, fl, cf,displayFrames);
-
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.set(displayFrames.mCurrent);
- } else {
- vf.set(cf);
- }
- } else if (attached != null) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
- + "): attached to " + attached);
- // A child window should be placed inside of the same visible
- // frame that its parent had.
- setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf,
- displayFrames);
- } else {
- if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
- "): normal window");
- // Otherwise, a normal window must be placed inside the content
- // of all screen decorations.
- if (type == TYPE_STATUS_BAR_PANEL) {
- // Status bar panels can go on
- // top of the status bar. They are protected by the STATUS_BAR_SERVICE
- // permission, so they have the same privileges as the status bar itself.
- cf.set(displayFrames.mRestricted);
- of.set(displayFrames.mRestricted);
- df.set(displayFrames.mRestricted);
- pf.set(displayFrames.mRestricted);
- } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
- // These dialogs are stable to interim decor changes.
- cf.set(displayFrames.mStable);
- of.set(displayFrames.mStable);
- df.set(displayFrames.mStable);
- pf.set(displayFrames.mStable);
- } else {
- pf.set(displayFrames.mContent);
- if (win.isVoiceInteraction()) {
- cf.set(displayFrames.mVoiceContent);
- of.set(displayFrames.mVoiceContent);
- df.set(displayFrames.mVoiceContent);
- } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- cf.set(displayFrames.mDock);
- of.set(displayFrames.mDock);
- df.set(displayFrames.mDock);
- } else {
- cf.set(displayFrames.mContent);
- of.set(displayFrames.mContent);
- df.set(displayFrames.mContent);
- }
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.set(displayFrames.mCurrent);
- } else {
- vf.set(cf);
- }
- }
- }
- }
-
- final int cutoutMode = attrs.layoutInDisplayCutoutMode;
- final boolean attachedInParent = attached != null && !layoutInScreen;
- final boolean requestedHideNavigation =
- (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
-
- // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get
- // cropped / shifted to the displayFrame in WindowState.
- final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
- && type != TYPE_BASE_APPLICATION;
-
- // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
- // the cutout safe zone.
- if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
- final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
- displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
- if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
- && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
- // At the top we have the status bar, so apps that are
- // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
- // already expect that there's an inset there and we don't need to exclude
- // the window from that area.
- displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
- }
- if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation
- && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
- // Same for the navigation bar.
- switch (mNavigationBarPosition) {
- case NAV_BAR_BOTTOM:
- displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
- break;
- case NAV_BAR_RIGHT:
- displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
- break;
- case NAV_BAR_LEFT:
- displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
- break;
- }
- }
- if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) {
- // The IME can always extend under the bottom cutout if the navbar is there.
- displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
- }
- // Windows that are attached to a parent and laid out in said parent already avoid
- // the cutout according to that parent and don't need to be further constrained.
- // Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
- // They will later be cropped or shifted using the displayFrame in WindowState,
- // which prevents overlap with the DisplayCutout.
- if (!attachedInParent && !floatingInScreenWindow) {
- sTmpRect.set(pf);
- pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
- windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
- }
- // Make sure that NO_LIMITS windows clipped to the display don't extend under the
- // cutout.
- df.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
- }
-
- // Content should never appear in the cutout.
- cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
-
- // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
- // Also, we don't allow windows in multi-window mode to extend out of the screen.
- if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
- && !win.isInMultiWindowMode()) {
- df.left = df.top = -10000;
- df.right = df.bottom = 10000;
- if (type != TYPE_WALLPAPER) {
- of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000;
- of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
- }
- }
-
- // If the device has a chin (e.g. some watches), a dead area at the bottom of the screen we
- // need to provide information to the clients that want to pretend that you can draw there.
- // We only want to apply outsets to certain types of windows. For example, we never want to
- // apply the outsets to floating dialogs, because they wouldn't make sense there.
- final boolean useOutsets = shouldUseOutsets(attrs, fl);
- if (isDefaultDisplay && useOutsets) {
- final Rect osf = windowFrames.mOutsetFrame;
- osf.set(cf.left, cf.top, cf.right, cf.bottom);
- windowFrames.setHasOutsets(true);
- int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
- if (outset > 0) {
- int rotation = displayFrames.mRotation;
- if (rotation == Surface.ROTATION_0) {
- osf.bottom += outset;
- } else if (rotation == Surface.ROTATION_90) {
- osf.right += outset;
- } else if (rotation == Surface.ROTATION_180) {
- osf.top -= outset;
- } else if (rotation == Surface.ROTATION_270) {
- osf.left -= outset;
- }
- if (DEBUG_LAYOUT) Slog.v(TAG, "applying bottom outset of " + outset
- + " with rotation " + rotation + ", result: " + osf);
- }
- }
-
- if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
- + ": sim=#" + Integer.toHexString(sim)
- + " attach=" + attached + " type=" + type
- + String.format(" flags=0x%08x", fl)
- + " pf=" + pf.toShortString() + " df=" + df.toShortString()
- + " of=" + of.toShortString()
- + " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
- + " dcf=" + dcf.toShortString()
- + " sf=" + sf.toShortString()
- + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win);
-
- if (!sTmpLastParentFrame.equals(pf)) {
- windowFrames.setContentChanged(true);
- }
-
- win.computeFrameLw();
- // Dock windows carve out the bottom of the screen, so normal windows
- // can't appear underneath them.
- if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
- && !win.getGivenInsetsPendingLw()) {
- offsetInputMethodWindowLw(win, displayFrames);
- }
- if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
- && !win.getGivenInsetsPendingLw()) {
- offsetVoiceInputWindowLw(win, displayFrames);
- }
- }
-
- private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) {
- // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area.
- df.set(displayFrames.mOverscan);
- pf.set(displayFrames.mOverscan);
- cf.set(displayFrames.mUnrestricted);
- of.set(displayFrames.mUnrestricted);
- }
-
- private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
- int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
- top += win.getGivenContentInsetsLw().top;
- displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
- displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
- top = win.getVisibleFrameLw().top;
- top += win.getGivenVisibleInsetsLw().top;
- displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top);
- if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom="
- + displayFrames.mDock.bottom + " mContentBottom="
- + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom);
- }
-
- private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) {
- int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
- top += win.getGivenContentInsetsLw().top;
- displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
- }
-
- /** {@inheritDoc} */
- @Override
- public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
- mTopFullscreenOpaqueWindowState = null;
- mTopFullscreenOpaqueOrDimmingWindowState = null;
- mTopDockedOpaqueWindowState = null;
- mTopDockedOpaqueOrDimmingWindowState = null;
- mForceStatusBar = false;
- mForceStatusBarFromKeyguard = false;
- mForceStatusBarTransparent = false;
- mForcingShowNavBar = false;
- mForcingShowNavBarLayer = -1;
-
- mAllowLockscreenWhenOn = false;
- mShowingDream = false;
- mWindowSleepTokenNeeded = false;
- }
-
- /** {@inheritDoc} */
- @Override
- public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
- WindowState attached, WindowState imeTarget) {
- final boolean affectsSystemUi = win.canAffectSystemUiFlags();
- if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
- applyKeyguardPolicyLw(win, imeTarget);
- final int fl = PolicyControl.getWindowFlags(win, attrs);
- if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi
- && attrs.type == TYPE_INPUT_METHOD) {
- mForcingShowNavBar = true;
- mForcingShowNavBarLayer = win.getSurfaceLayer();
- }
- if (attrs.type == TYPE_STATUS_BAR) {
- if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
- mForceStatusBarFromKeyguard = true;
- }
- if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
- mForceStatusBarTransparent = true;
- }
- }
-
- boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
- && attrs.type < FIRST_SYSTEM_WINDOW;
- final int windowingMode = win.getWindowingMode();
- final boolean inFullScreenOrSplitScreenSecondaryWindowingMode =
- windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) {
- if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
- mForceStatusBar = true;
- }
- if (attrs.type == TYPE_DREAM) {
- // If the lockscreen was showing when the dream started then wait
- // for the dream to draw before hiding the lockscreen.
- if (!mDreamingLockscreen
- || (win.isVisibleLw() && win.hasDrawnLw())) {
- mShowingDream = true;
- appWindow = true;
- }
- }
-
- // For app windows that are not attached, we decide if all windows in the app they
- // represent should be hidden or if we should hide the lockscreen. For attached app
- // windows we defer the decision to the window it is attached to.
- if (appWindow && attached == null) {
- if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
- mTopFullscreenOpaqueWindowState = win;
- if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
- mTopFullscreenOpaqueOrDimmingWindowState = win;
- }
- if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
- mAllowLockscreenWhenOn = true;
- }
- }
- }
- }
-
- // Voice interaction overrides both top fullscreen and top docked.
- if (affectsSystemUi && win.getAttrs().type == TYPE_VOICE_INTERACTION) {
- if (mTopFullscreenOpaqueWindowState == null) {
- mTopFullscreenOpaqueWindowState = win;
- if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
- mTopFullscreenOpaqueOrDimmingWindowState = win;
- }
- }
- if (mTopDockedOpaqueWindowState == null) {
- mTopDockedOpaqueWindowState = win;
- if (mTopDockedOpaqueOrDimmingWindowState == null) {
- mTopDockedOpaqueOrDimmingWindowState = win;
- }
- }
- }
-
- // Keep track of the window if it's dimming but not necessarily fullscreen.
- if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi
- && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
- mTopFullscreenOpaqueOrDimmingWindowState = win;
- }
-
- // We need to keep track of the top "fullscreen" opaque window for the docked stack
- // separately, because both the "real fullscreen" opaque window and the one for the docked
- // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
- if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
- && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mTopDockedOpaqueWindowState = win;
- if (mTopDockedOpaqueOrDimmingWindowState == null) {
- mTopDockedOpaqueOrDimmingWindowState = win;
- }
- }
-
- // Also keep track of any windows that are dimming but not necessarily fullscreen in the
- // docked stack.
- if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming()
- && 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;
- }
- }
-
- private void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) {
+ public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) {
if (canBeHiddenByKeyguardLw(win)) {
if (shouldBeHiddenByKeyguard(win, imeTarget)) {
win.hideLw(false /* doAnimation */);
@@ -5441,148 +3373,9 @@
/** {@inheritDoc} */
@Override
- public int finishPostLayoutPolicyLw() {
- int changes = 0;
- boolean topIsFullscreen = false;
-
- final WindowManager.LayoutParams lp = (mTopFullscreenOpaqueWindowState != null)
- ? mTopFullscreenOpaqueWindowState.getAttrs()
- : null;
-
- // If we are not currently showing a dream then remember the current
- // lockscreen state. We will use this to determine whether the dream
- // started while the lockscreen was showing and remember this state
- // while the dream is showing.
- if (!mShowingDream) {
- mDreamingLockscreen = isKeyguardShowingAndNotOccluded();
- if (mDreamingSleepTokenNeeded) {
- mDreamingSleepTokenNeeded = false;
- mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget();
- }
- } else {
- if (!mDreamingSleepTokenNeeded) {
- mDreamingSleepTokenNeeded = true;
- mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget();
- }
- }
-
- if (mStatusBar != null) {
- if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar
- + " forcefkg=" + mForceStatusBarFromKeyguard
- + " top=" + mTopFullscreenOpaqueWindowState);
- boolean shouldBeTransparent = mForceStatusBarTransparent
- && !mForceStatusBar
- && !mForceStatusBarFromKeyguard;
- if (!shouldBeTransparent) {
- mStatusBarController.setShowTransparent(false /* transparent */);
- } else if (!mStatusBar.isVisibleLw()) {
- mStatusBarController.setShowTransparent(true /* transparent */);
- }
-
- boolean statusBarForcesShowingNavigation
- = (mStatusBar.getAttrs().privateFlags
- & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
- boolean topAppHidesStatusBar = topAppHidesStatusBar();
- if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent
- || statusBarForcesShowingNavigation) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
- if (mStatusBarController.setBarShowingLw(true)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
- // Maintain fullscreen layout until incoming animation is complete.
- topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw();
- // Transient status bar is not allowed if status bar is on lockscreen or status bar
- // is expecting the navigation keys from the user.
- if ((mForceStatusBarFromKeyguard || statusBarForcesShowingNavigation)
- && mStatusBarController.isTransientShowing()) {
- mStatusBarController.updateVisibilityLw(false /*transientAllowed*/,
- mLastSystemUiFlags, mLastSystemUiFlags);
- }
- } else if (mTopFullscreenOpaqueWindowState != null) {
- topIsFullscreen = topAppHidesStatusBar;
- // The subtle difference between the window for mTopFullscreenOpaqueWindowState
- // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
- // has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the
- // case though.
- if (mStatusBarController.isTransientShowing()) {
- if (mStatusBarController.setBarShowingLw(true)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
- } else if (topIsFullscreen
- && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM)
- && !mWindowManagerInternal.isStackVisible(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
- if (mStatusBarController.setBarShowingLw(false)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- } else {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding");
- }
- } else {
- if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen");
- if (mStatusBarController.setBarShowingLw(true)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
- topAppHidesStatusBar = false;
- }
- }
- mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar);
- }
-
- if (mTopIsFullscreen != topIsFullscreen) {
- if (!topIsFullscreen) {
- // Force another layout when status bar becomes fully shown.
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
- mTopIsFullscreen = topIsFullscreen;
- }
-
- if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
- // If the navigation bar has been hidden or shown, we need to do another
- // layout pass to update that window.
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
-
- if (mShowingDream != mLastShowingDream) {
- mLastShowingDream = mShowingDream;
- mWindowManagerFuncs.notifyShowingDreamChanged();
- }
-
- updateWindowSleepToken();
-
- // update since mAllowLockscreenWhenOn might have changed
- updateLockScreenTimeout();
- return changes;
- }
-
- private void updateWindowSleepToken() {
- if (mWindowSleepTokenNeeded && !mLastWindowSleepTokenNeeded) {
- mHandler.removeCallbacks(mReleaseSleepTokenRunnable);
- mHandler.post(mAcquireSleepTokenRunnable);
- } else if (!mWindowSleepTokenNeeded && mLastWindowSleepTokenNeeded) {
- mHandler.removeCallbacks(mAcquireSleepTokenRunnable);
- mHandler.post(mReleaseSleepTokenRunnable);
- }
- mLastWindowSleepTokenNeeded = mWindowSleepTokenNeeded;
- }
-
- /**
- * @return Whether the top app should hide the statusbar based on the top fullscreen opaque
- * window.
- */
- private boolean topAppHidesStatusBar() {
- if (mTopFullscreenOpaqueWindowState == null) {
- return false;
- }
- final int fl = PolicyControl.getWindowFlags(null,
- mTopFullscreenOpaqueWindowState.getAttrs());
- if (localLOGV) {
- Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw());
- Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
- + " lp.flags=0x" + Integer.toHexString(fl));
- }
- return (fl & LayoutParams.FLAG_FULLSCREEN) != 0
- || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+ public void setKeyguardCandidateLw(WindowState win) {
+ mKeyguardCandidate = win;
+ setKeyguardOccludedLw(mKeyguardOccluded, true /* force */);
}
/**
@@ -5598,19 +3391,19 @@
if (!isOccluded && changed && showing) {
mKeyguardOccluded = false;
mKeyguardDelegate.setOccluded(false, true /* animate */);
- if (mStatusBar != null) {
- mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD;
+ if (mKeyguardCandidate != null) {
+ mKeyguardCandidate.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD;
if (!mKeyguardDelegate.hasLockscreenWallpaper()) {
- mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+ mKeyguardCandidate.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
}
}
return true;
} else if (isOccluded && changed && showing) {
mKeyguardOccluded = true;
mKeyguardDelegate.setOccluded(true, false /* animate */);
- if (mStatusBar != null) {
- mStatusBar.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD;
- mStatusBar.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
+ if (mKeyguardCandidate != null) {
+ mKeyguardCandidate.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD;
+ mKeyguardCandidate.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
}
return true;
} else if (changed) {
@@ -5622,28 +3415,6 @@
}
}
- private boolean isStatusBarKeyguard() {
- return mStatusBar != null
- && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
- }
-
- @Override
- public boolean allowAppAnimationsLw() {
- return !mShowingDream;
- }
-
- @Override
- public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
- mFocusedWindow = newFocus;
- mLastFocusedWindow = lastFocus;
- if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
- // If the navigation bar has been hidden or shown, we need to do another
- // layout pass to update that window.
- return FINISH_LAYOUT_REDO_LAYOUT;
- }
- return 0;
- }
-
/** {@inheritDoc} */
@Override
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
@@ -6469,57 +4240,11 @@
// current user.
mSettingsObserver.onChange(false);
mDefaultDisplayRotation.onUserSwitch();
-
- // force a re-application of focused window sysui visibility.
- // the window may never have been shown for this user
- // e.g. the keyguard when going through the new-user setup flow
- synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
- mLastSystemUiFlags = 0;
- updateSystemUiVisibilityLw();
- }
+ mWindowManagerFuncs.onUserSwitched();
}
}
};
- private final Runnable mHiddenNavPanic = new Runnable() {
- @Override
- public void run() {
- synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
- if (!isUserSetupComplete()) {
- // Swipe-up for navigation bar is disabled during setup
- return;
- }
- mPendingPanicGestureUptime = SystemClock.uptimeMillis();
- if (!isNavBarEmpty(mLastSystemUiFlags)) {
- mNavigationBarController.showTransient();
- }
- }
- }
- };
-
- private void requestTransientBars(WindowState swipeTarget) {
- synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
- if (!isUserSetupComplete()) {
- // Swipe-up for navigation bar is disabled during setup
- return;
- }
- boolean sb = mStatusBarController.checkShowTransientBarLw();
- boolean nb = mNavigationBarController.checkShowTransientBarLw()
- && !isNavBarEmpty(mLastSystemUiFlags);
- if (sb || nb) {
- // Don't show status bar when swiping on already visible navigation bar
- if (!nb && swipeTarget == mNavigationBar) {
- if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
- return;
- }
- if (sb) mStatusBarController.showTransient();
- if (nb) mNavigationBarController.showTransient();
- mImmersiveModeConfirmation.confirmCurrentPrompt();
- updateSystemUiVisibilityLw();
- }
- }
- }
-
// Called on the PowerManager's Notifier thread.
@Override
public void startedGoingToSleep(int why) {
@@ -6852,11 +4577,6 @@
}
@Override
- public boolean isShowingDreamLw() {
- return mShowingDream;
- }
-
- @Override
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
if (mKeyguardDelegate != null) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.startKeyguardExitAnimation");
@@ -6864,85 +4584,6 @@
}
}
- @Override
- public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
- DisplayCutout displayCutout, Rect outInsets) {
- outInsets.setEmpty();
-
- // Navigation bar and status bar.
- getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets);
- outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]);
- }
-
- @Override
- public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
- DisplayCutout displayCutout, Rect outInsets) {
- outInsets.setEmpty();
-
- // Only navigation bar
- if (mDefaultDisplayPolicy.hasNavigationBar()) {
- int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
- if (position == NAV_BAR_BOTTOM) {
- outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
- } else if (position == NAV_BAR_RIGHT) {
- outInsets.right = getNavigationBarWidth(displayRotation, mUiMode);
- } else if (position == NAV_BAR_LEFT) {
- outInsets.left = getNavigationBarWidth(displayRotation, mUiMode);
- }
- }
-
- if (displayCutout != null) {
- outInsets.left += displayCutout.getSafeInsetLeft();
- outInsets.top += displayCutout.getSafeInsetTop();
- outInsets.right += displayCutout.getSafeInsetRight();
- outInsets.bottom += displayCutout.getSafeInsetBottom();
- }
- }
-
- @Override
- public boolean isNavBarForcedShownLw(WindowState windowState) {
- return mForceShowSystemBars;
- }
-
- @Override
- public int getNavBarPosition() {
- // TODO(multi-display): Support system decor on secondary displays.
- return mNavigationBarPosition;
- }
-
- @Override
- public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
- int displayHeight, int displayRotation) {
- final int barPosition = navigationBarPosition(displayWidth, displayHeight, displayRotation);
- return isDockSideAllowed(dockSide, originalDockSide, barPosition,
- mDefaultDisplayPolicy.navigationBarCanMove());
- }
-
- @VisibleForTesting
- static boolean isDockSideAllowed(int dockSide, int originalDockSide,
- int navBarPosition, boolean navigationBarCanMove) {
- if (dockSide == DOCKED_TOP) {
- return true;
- }
-
- if (navigationBarCanMove) {
- // Only allow the dockside opposite to the nav bar position in landscape
- return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT
- || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT;
- }
-
- // Side is the same as original side
- if (dockSide == originalDockSide) {
- return true;
- }
-
- // Only if original docked side was top in portrait will allow left for landscape
- if (dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP) {
- return true;
- }
- return false;
- }
-
void sendCloseSystemWindows() {
PhoneWindow.sendCloseSystemWindows(mContext, null);
}
@@ -7009,9 +4650,6 @@
}
}
- mSystemGestures.systemReady();
- mImmersiveModeConfirmation.systemReady();
-
mAutofillManagerInternal = LocalServices.getService(AutofillManagerInternal.class);
}
@@ -7173,10 +4811,22 @@
mHandler.post(mScreenLockTimeout);
}
+ // TODO (b/113840485): Move this logic to DisplayPolicy when lockscreen supports multi-display.
+ @Override
+ public void setAllowLockscreenWhenOn(int displayId, boolean allow) {
+ if (allow) {
+ mAllowLockscreenWhenOnDisplays.add(displayId);
+ } else {
+ mAllowLockscreenWhenOnDisplays.remove(displayId);
+ }
+ updateLockScreenTimeout();
+ }
+
private void updateLockScreenTimeout() {
synchronized (mScreenLockTimeout) {
- final boolean enable = (mAllowLockscreenWhenOn && mDefaultDisplayPolicy.isAwake() &&
- mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId));
+ final boolean enable = !mAllowLockscreenWhenOnDisplays.isEmpty()
+ && mDefaultDisplayPolicy.isAwake()
+ && mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId);
if (mLockScreenTimerActive != enable) {
if (enable) {
if (localLOGV) Log.v(TAG, "setting lockscreen timer");
@@ -7192,21 +4842,6 @@
}
// TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
- private void updateDreamingSleepToken(boolean acquire) {
- if (acquire) {
- if (mDreamingSleepToken == null) {
- mDreamingSleepToken = mActivityTaskManagerInternal.acquireSleepToken(
- "Dream", DEFAULT_DISPLAY);
- }
- } else {
- if (mDreamingSleepToken != null) {
- mDreamingSleepToken.release();
- mDreamingSleepToken = null;
- }
- }
- }
-
- // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
private void updateScreenOffSleepToken(boolean acquire) {
if (acquire) {
if (mScreenOffSleepToken == null) {
@@ -7254,6 +4889,11 @@
}
}
+ @Override
+ public int getUiMode() {
+ return mUiMode;
+ }
+
void updateRotation(boolean alwaysSendConfiguration) {
try {
// Set orientation on WindowManager.
@@ -7510,380 +5150,6 @@
}
}
- private int updateSystemUiVisibilityLw() {
- // If there is no window focused, there will be nobody to handle the events
- // anyway, so just hang on in whatever state we're in until things settle down.
- WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
- : mTopFullscreenOpaqueWindowState;
- if (winCandidate == null) {
- return 0;
- }
-
- // The immersive mode confirmation should never affect the system bar visibility, otherwise
- // it will unhide the navigation bar and hide itself.
- if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
-
- // The immersive mode confirmation took the focus from mLastFocusedWindow which was
- // controlling the system ui visibility. So if mLastFocusedWindow can still receive
- // keys, we let it keep controlling the visibility.
- final boolean lastFocusCanReceiveKeys =
- (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
- winCandidate = isStatusBarKeyguard() ? mStatusBar
- : lastFocusCanReceiveKeys ? mLastFocusedWindow
- : mTopFullscreenOpaqueWindowState;
- if (winCandidate == null) {
- return 0;
- }
- }
- final WindowState win = winCandidate;
- if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mKeyguardOccluded) {
- // We are updating at a point where the keyguard has gotten
- // focus, but we were last in a state where the top window is
- // hiding it. This is probably because the keyguard as been
- // shown while the top window was displayed, so we want to ignore
- // it here because this is just a very transient change and it
- // will quickly lose focus once it correctly gets hidden.
- return 0;
- }
-
- int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
- & ~mResettingSystemUiFlags
- & ~mForceClearedSystemUiFlags;
- if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
- tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
- }
-
- final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
- mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
- final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
- mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
- mWindowManagerFuncs.getStackBounds(
- WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
- mWindowManagerFuncs.getStackBounds(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
- final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
- final int diff = visibility ^ mLastSystemUiFlags;
- final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
- final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
- final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
- if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
- && mFocusedApp == win.getAppToken()
- && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
- && mLastDockedStackBounds.equals(mDockedStackBounds)) {
- return 0;
- }
- mLastSystemUiFlags = visibility;
- mLastFullscreenStackSysUiFlags = fullscreenVisibility;
- mLastDockedStackSysUiFlags = dockedVisibility;
- mLastFocusNeedsMenu = needsMenu;
- mFocusedApp = win.getAppToken();
- final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
- final Rect dockedStackBounds = new Rect(mDockedStackBounds);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
- if (statusbar != null) {
- statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
- dockedVisibility, 0xffffffff, fullscreenStackBounds,
- dockedStackBounds, win.toString());
- statusbar.topAppWindowChanged(needsMenu);
- }
- }
- });
- return diff;
- }
-
- private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
- final boolean onKeyguard = isStatusBarKeyguard() && !mKeyguardOccluded;
- final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming;
- if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) {
- // If the top fullscreen-or-dimming window is also the top fullscreen, respect
- // its light flag.
- vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
- & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- } else if (statusColorWin != null && statusColorWin.isDimming()) {
- // Otherwise if it's dimming, clear the light flag.
- vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- }
- return vis;
- }
-
- @VisibleForTesting
- @Nullable
- static WindowState chooseNavigationColorWindowLw(WindowState opaque,
- WindowState opaqueOrDimming, WindowState imeWindow,
- @NavigationBarPosition int navBarPosition) {
- // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME
- // window can be navigation color window.
- final boolean imeWindowCanNavColorWindow = imeWindow != null
- && imeWindow.isVisibleLw()
- && navBarPosition == NAV_BAR_BOTTOM
- && (PolicyControl.getWindowFlags(imeWindow, null)
- & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
-
- if (opaque != null && opaqueOrDimming == opaque) {
- // If the top fullscreen-or-dimming window is also the top fullscreen, respect it
- // unless IME window is also eligible, since currently the IME window is always show
- // above the opaque fullscreen app window, regardless of the IME target window.
- // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.
- return imeWindowCanNavColorWindow ? imeWindow : opaque;
- }
-
- if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {
- // No dimming window is involved. Determine the result only with the IME window.
- return imeWindowCanNavColorWindow ? imeWindow : null;
- }
-
- if (!imeWindowCanNavColorWindow) {
- // No IME window is involved. Determine the result only with opaqueOrDimming.
- return opaqueOrDimming;
- }
-
- // The IME window and the dimming window are competing. Check if the dimming window can be
- // IME target or not.
- if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) {
- // The IME window is above the dimming window.
- return imeWindow;
- } else {
- // The dimming window is above the IME window.
- return opaqueOrDimming;
- }
- }
-
- @VisibleForTesting
- static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming,
- WindowState imeWindow, WindowState navColorWin) {
-
- if (navColorWin != null) {
- if (navColorWin == imeWindow || navColorWin == opaque) {
- // Respect the light flag.
- vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
- & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
- // Clear the light flag for dimming window.
- vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- }
- }
- return vis;
- }
-
- private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
- final boolean dockedStackVisible =
- mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- final boolean freeformStackVisible =
- mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM);
- final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();
-
- // We need to force system bars when the docked stack is visible, when the freeform stack
- // is visible but also when we are resizing for the transitions when docked stack
- // visibility changes.
- mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
- final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
-
- // apply translucent bar vis flags
- WindowState fullscreenTransWin = isStatusBarKeyguard() && !mKeyguardOccluded
- ? mStatusBar
- : mTopFullscreenOpaqueWindowState;
- vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
- vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
- final int dockedVis = mStatusBarController.applyTranslucentFlagLw(
- mTopDockedOpaqueWindowState, 0, 0);
-
- final boolean fullscreenDrawsStatusBarBackground =
- drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);
- final boolean dockedDrawsStatusBarBackground =
- drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);
-
- // prevent status bar interaction from clearing certain flags
- int type = win.getAttrs().type;
- boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
- if (statusBarHasFocus && !isStatusBarKeyguard()) {
- int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_IMMERSIVE
- | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- if (mKeyguardOccluded) {
- flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
- }
- vis = (vis & ~flags) | (oldVis & flags);
- }
-
- if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
- vis |= View.STATUS_BAR_TRANSPARENT;
- vis &= ~View.STATUS_BAR_TRANSLUCENT;
- } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)
- || forceOpaqueStatusBar) {
- vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
- }
-
- vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);
-
- // update status bar
- boolean immersiveSticky =
- (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
- final boolean hideStatusBarWM =
- mTopFullscreenOpaqueWindowState != null
- && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
- & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
- final boolean hideStatusBarSysui =
- (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
- final boolean hideNavBarSysui =
- (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
-
- final boolean transientStatusBarAllowed = mStatusBar != null
- && (statusBarHasFocus || (!mForceShowSystemBars
- && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
-
- final boolean transientNavBarAllowed = mNavigationBar != null
- && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;
-
- final long now = SystemClock.uptimeMillis();
- final boolean pendingPanic = mPendingPanicGestureUptime != 0
- && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
- if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard()
- && mDefaultDisplayPolicy.isKeyguardDrawComplete()) {
- // The user performed the panic gesture recently, we're about to hide the bars,
- // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
- mPendingPanicGestureUptime = 0;
- mStatusBarController.showTransient();
- if (!isNavBarEmpty(vis)) {
- mNavigationBarController.showTransient();
- }
- }
-
- final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
- && !transientStatusBarAllowed && hideStatusBarSysui;
- final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
- && !transientNavBarAllowed;
- if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {
- // clear the clearable flags instead
- clearClearableFlagsLw();
- vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
- }
-
- final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
- immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
- final boolean navAllowedHidden = immersive || immersiveSticky;
-
- if (hideNavBarSysui && !navAllowedHidden
- && getWindowLayerLw(win) > getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) {
- // We can't hide the navbar from this window otherwise the input consumer would not get
- // the input events.
- vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
- }
-
- vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);
-
- // update navigation bar
- boolean oldImmersiveMode = isImmersiveMode(oldVis);
- boolean newImmersiveMode = isImmersiveMode(vis);
- if (win != null && oldImmersiveMode != newImmersiveMode) {
- final String pkg = win.getOwningPackage();
- mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
- isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility()));
- }
-
- vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
-
- final WindowState navColorWin = chooseNavigationColorWindowLw(
- mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
- mWindowManagerFuncs.getInputMethodWindowLw(), mNavigationBarPosition);
- vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
- mTopFullscreenOpaqueOrDimmingWindowState,
- mWindowManagerFuncs.getInputMethodWindowLw(), navColorWin);
-
- return vis;
- }
-
- private boolean drawsStatusBarBackground(int vis, WindowState win) {
- if (!mStatusBarController.isTransparentAllowed(win)) {
- return false;
- }
- if (win == null) {
- return true;
- }
-
- final boolean drawsSystemBars =
- (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
- final boolean forceDrawsSystemBars =
- (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
-
- return forceDrawsSystemBars || drawsSystemBars && (vis & View.STATUS_BAR_TRANSLUCENT) == 0;
- }
-
- /**
- * @return the current visibility flags with the nav-bar opacity related flags toggled based
- * on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
- */
- private int configureNavBarOpacity(int visibility, boolean dockedStackVisible,
- boolean freeformStackVisible, boolean isDockedDividerResizing) {
- if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
- if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) {
- visibility = setNavBarOpaqueFlag(visibility);
- }
- } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
- if (isDockedDividerResizing) {
- visibility = setNavBarOpaqueFlag(visibility);
- } else if (freeformStackVisible) {
- visibility = setNavBarTranslucentFlag(visibility);
- } else {
- visibility = setNavBarOpaqueFlag(visibility);
- }
- }
-
- if (!areTranslucentBarsAllowed()) {
- visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT;
- }
- return visibility;
- }
-
- private int setNavBarOpaqueFlag(int visibility) {
- return visibility &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT);
- }
-
- private int setNavBarTranslucentFlag(int visibility) {
- visibility &= ~View.NAVIGATION_BAR_TRANSPARENT;
- return visibility |= View.NAVIGATION_BAR_TRANSLUCENT;
- }
-
- private void clearClearableFlagsLw() {
- int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS;
- if (newVal != mResettingSystemUiFlags) {
- mResettingSystemUiFlags = newVal;
- mWindowManagerFuncs.reevaluateStatusBarVisibility();
- }
- }
-
- private boolean isImmersiveMode(int vis) {
- final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
- return mNavigationBar != null
- && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
- && (vis & flags) != 0
- && canHideNavigationBar();
- }
-
- private static boolean isNavBarEmpty(int systemUiFlags) {
- final int disableNavigationBar = (View.STATUS_BAR_DISABLE_HOME
- | View.STATUS_BAR_DISABLE_BACK
- | View.STATUS_BAR_DISABLE_RECENT);
-
- return (systemUiFlags & disableNavigationBar) == disableNavigationBar;
- }
-
- /**
- * @return whether the navigation or status bar can be made translucent
- *
- * This should return true unless touch exploration is not enabled or
- * R.boolean.config_enableTranslucentDecor is false.
- */
- private boolean areTranslucentBarsAllowed() {
- return mTranslucentDecorEnabled;
- }
-
// Use this instead of checking config_showNavigationBar so that it can be consistently
// overridden by qemu.hw.mainkeys in the emulator.
@Override
@@ -7926,44 +5192,8 @@
}
@Override
- public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation,
- int newRotation) {
- // For the upside down rotation we don't rotate seamlessly as the navigation
- // bar moves position.
- // Note most apps (using orientation:sensor or user as opposed to fullSensor)
- // will not enter the reverse portrait orientation, so actually the
- // orientation won't change at all.
- if (oldRotation == displayRotation.getUpsideDownRotation()
- || newRotation == displayRotation.getUpsideDownRotation()) {
- return false;
- }
- // If the navigation bar can't change sides, then it will
- // jump when we change orientations and we don't rotate
- // seamlessly.
- if (!displayRotation.getDisplayPolicy().navigationBarCanMove()) {
- return false;
- }
-
- final WindowState w = mTopFullscreenOpaqueWindowState;
- if (w != mFocusedWindow) {
- return false;
- }
-
- // We only enable seamless rotation if the top window has requested
- // it and is in the fullscreen opaque state. Seamless rotation
- // requires freezing various Surface states and won't work well
- // with animations, so we disable it in the animation case for now.
- if (w != null && !w.isAnimatingLw() &&
- w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) {
- return true;
- }
- return false;
- }
-
- @Override
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(LAST_SYSTEM_UI_FLAGS, mLastSystemUiFlags);
proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
proto.write(ROTATION, mDefaultDisplayRotation.getUserRotation());
proto.write(ORIENTATION, mDefaultDisplayRotation.getCurrentAppOrientation());
@@ -7971,30 +5201,9 @@
proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete());
proto.write(WINDOW_MANAGER_DRAW_COMPLETE,
mDefaultDisplayPolicy.isWindowManagerDrawComplete());
- if (mFocusedApp != null) {
- proto.write(FOCUSED_APP_TOKEN, mFocusedApp.toString());
- }
- if (mFocusedWindow != null) {
- mFocusedWindow.writeIdentifierToProto(proto, FOCUSED_WINDOW);
- }
- if (mTopFullscreenOpaqueWindowState != null) {
- mTopFullscreenOpaqueWindowState.writeIdentifierToProto(
- proto, TOP_FULLSCREEN_OPAQUE_WINDOW);
- }
- if (mTopFullscreenOpaqueOrDimmingWindowState != null) {
- mTopFullscreenOpaqueOrDimmingWindowState.writeIdentifierToProto(
- proto, TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW);
- }
proto.write(KEYGUARD_OCCLUDED, mKeyguardOccluded);
proto.write(KEYGUARD_OCCLUDED_CHANGED, mKeyguardOccludedChanged);
proto.write(KEYGUARD_OCCLUDED_PENDING, mPendingKeyguardOccluded);
- proto.write(FORCE_STATUS_BAR, mForceStatusBar);
- proto.write(FORCE_STATUS_BAR_FROM_KEYGUARD, mForceStatusBarFromKeyguard);
- mStatusBarController.writeToProto(proto, STATUS_BAR);
- mNavigationBarController.writeToProto(proto, NAVIGATION_BAR);
- if (mDefaultOrientationListener != null) {
- mDefaultOrientationListener.writeToProto(proto, ORIENTATION_LISTENER);
- }
if (mKeyguardDelegate != null) {
mKeyguardDelegate.writeToProto(proto, KEYGUARD_DELEGATE);
}
@@ -8008,19 +5217,6 @@
pw.print(" mSystemBooted="); pw.println(mSystemBooted);
pw.print(prefix); pw.print("mCameraLensCoverState=");
pw.println(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
- if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0
- || mForceClearedSystemUiFlags != 0) {
- pw.print(prefix); pw.print("mLastSystemUiFlags=0x");
- pw.print(Integer.toHexString(mLastSystemUiFlags));
- pw.print(" mResettingSystemUiFlags=0x");
- pw.print(Integer.toHexString(mResettingSystemUiFlags));
- pw.print(" mForceClearedSystemUiFlags=0x");
- pw.println(Integer.toHexString(mForceClearedSystemUiFlags));
- }
- if (mLastFocusNeedsMenu) {
- pw.print(prefix); pw.print("mLastFocusNeedsMenu=");
- pw.println(mLastFocusNeedsMenu);
- }
pw.print(prefix); pw.print("mWakeGestureEnabledSetting=");
pw.println(mWakeGestureEnabledSetting);
@@ -8083,50 +5279,11 @@
final int key = mDisplayHomeButtonHandlers.keyAt(i);
pw.println(mDisplayHomeButtonHandlers.get(key));
}
- pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer);
- pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer);
- pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
- pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
- pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
- if (mStatusBar != null) {
- pw.print(prefix); pw.print("mStatusBar=");
- pw.print(mStatusBar); pw.print(" isStatusBarKeyguard=");
- pw.println(isStatusBarKeyguard());
- }
- if (mNavigationBar != null) {
- pw.print(prefix); pw.print("mNavigationBar=");
- pw.println(mNavigationBar);
- }
- if (mFocusedWindow != null) {
- pw.print(prefix); pw.print("mFocusedWindow=");
- pw.println(mFocusedWindow);
- }
- if (mFocusedApp != null) {
- pw.print(prefix); pw.print("mFocusedApp=");
- pw.println(mFocusedApp);
- }
- if (mTopFullscreenOpaqueWindowState != null) {
- pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState=");
- pw.println(mTopFullscreenOpaqueWindowState);
- }
- if (mTopFullscreenOpaqueOrDimmingWindowState != null) {
- pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState=");
- pw.println(mTopFullscreenOpaqueOrDimmingWindowState);
- }
- if (mForcingShowNavBar) {
- pw.print(prefix); pw.print("mForcingShowNavBar=");
- pw.println(mForcingShowNavBar); pw.print( "mForcingShowNavBarLayer=");
- pw.println(mForcingShowNavBarLayer);
- }
- pw.print(prefix); pw.print("mTopIsFullscreen="); pw.print(mTopIsFullscreen);
- pw.print(" mKeyguardOccluded="); pw.println(mKeyguardOccluded);
- pw.print(prefix);
- pw.print("mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged);
+ pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(mKeyguardOccluded);
+ pw.print(" mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged);
pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded);
- pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
- pw.print(" mForceStatusBarFromKeyguard=");
- pw.println(mForceStatusBarFromKeyguard);
- pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn);
+ pw.print(prefix); pw.print("mAllowLockscreenWhenOnDisplays=");
+ pw.print(!mAllowLockscreenWhenOnDisplays.isEmpty());
pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
if (mHasFeatureLeanback) {
@@ -8139,16 +5296,10 @@
}
mGlobalKeyManager.dump(prefix, pw);
- mStatusBarController.dump(pw, prefix);
- mNavigationBarController.dump(pw, prefix);
- PolicyControl.dump(prefix, pw);
if (mWakeGestureListener != null) {
mWakeGestureListener.dump(pw, prefix);
}
- if (mDefaultOrientationListener != null) {
- mDefaultOrientationListener.dump(pw, prefix);
- }
if (mBurnInProtectionHelper != null) {
mBurnInProtectionHelper.dump(prefix, pw);
}
@@ -8310,11 +5461,6 @@
}
@Override
- public void onLockTaskStateChangedLw(int lockTaskState) {
- mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState);
- }
-
- @Override
public boolean setAodShowing(boolean aodShowing) {
if (mAodShowing != aodShowing) {
mAodShowing = aodShowing;
diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java
deleted file mode 100644
index e6e4d7f..0000000
--- a/services/core/java/com/android/server/policy/StatusBarController.java
+++ /dev/null
@@ -1,127 +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
- */
-
-package com.android.server.policy;
-
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
-import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
-import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-
-import android.app.StatusBarManager;
-import android.os.IBinder;
-import android.os.SystemClock;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.Interpolator;
-import android.view.animation.TranslateAnimation;
-
-import com.android.server.LocalServices;
-import com.android.server.statusbar.StatusBarManagerInternal;
-
-/**
- * Implements status bar specific behavior.
- */
-public class StatusBarController extends BarController {
-
- private final AppTransitionListener mAppTransitionListener
- = new AppTransitionListener() {
-
- @Override
- public void onAppTransitionPendingLocked() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- StatusBarManagerInternal statusbar = getStatusBarInternal();
- if (statusbar != null) {
- statusbar.appTransitionPending();
- }
- }
- });
- }
-
- @Override
- public int onAppTransitionStartingLocked(int transit, IBinder openToken,
- IBinder closeToken, long duration, long statusBarAnimationStartTime,
- long statusBarAnimationDuration) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- StatusBarManagerInternal statusbar = getStatusBarInternal();
- if (statusbar != null) {
- statusbar.appTransitionStarting(statusBarAnimationStartTime,
- statusBarAnimationDuration);
- }
- }
- });
- return 0;
- }
-
- @Override
- public void onAppTransitionCancelledLocked(int transit) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- StatusBarManagerInternal statusbar = getStatusBarInternal();
- if (statusbar != null) {
- statusbar.appTransitionCancelled();
- }
- }
- });
- }
-
- @Override
- public void onAppTransitionFinishedLocked(IBinder token) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- StatusBarManagerInternal statusbar = LocalServices.getService(
- StatusBarManagerInternal.class);
- if (statusbar != null) {
- statusbar.appTransitionFinished();
- }
- }
- });
- }
- };
-
- public StatusBarController() {
- super("StatusBar",
- View.STATUS_BAR_TRANSIENT,
- View.STATUS_BAR_UNHIDE,
- View.STATUS_BAR_TRANSLUCENT,
- StatusBarManager.WINDOW_STATUS_BAR,
- FLAG_TRANSLUCENT_STATUS,
- View.STATUS_BAR_TRANSPARENT);
- }
-
-
- public void setTopAppHidesStatusBar(boolean hidesStatusBar) {
- StatusBarManagerInternal statusbar = getStatusBarInternal();
- if (statusbar != null) {
- statusbar.setTopAppHidesStatusBar(hidesStatusBar);
- }
- }
-
- @Override
- protected boolean skipAnimation() {
- return mWin.getAttrs().height == MATCH_PARENT;
- }
-
- public AppTransitionListener getAppTransitionListener() {
- return mAppTransitionListener;
- }
-}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e1c4acf..3d474e3 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -65,7 +65,6 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.res.CompatibilityInfo;
@@ -78,7 +77,6 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
-import android.view.DisplayCutout;
import android.view.IApplicationToken;
import android.view.IWindowManager;
import android.view.InputEventReceiver;
@@ -90,7 +88,6 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
-import com.android.server.wm.DisplayFrames;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowFrames;
@@ -173,11 +170,6 @@
void onKeyguardOccludedChangedLw(boolean occluded);
/**
- * Called when the resource overlays change.
- */
- default void onOverlayChangedLw(DisplayContentInfo displayContentInfo) {}
-
- /**
* Interface to the Window Manager state associated with a particular
* window. You can hold on to an instance of this interface from the call
* to prepareAddWindow() until removeWindow().
@@ -526,11 +518,6 @@
public static final int CAMERA_LENS_COVERED = 1;
/**
- * Ask the window manager to re-evaluate the system UI flags.
- */
- public void reevaluateStatusBarVisibility();
-
- /**
* Add a input consumer which will consume all input events going to any window below it.
*/
public InputConsumer createInputConsumer(Looper looper, String name,
@@ -573,22 +560,12 @@
void unregisterPointerEventListener(PointerEventListener listener, int displayId);
/**
- * @return The content insets of the docked divider window.
- */
- int getDockedDividerInsetsLw();
-
- /**
* Retrieves the {@param outBounds} from the stack matching the {@param windowingMode} and
* {@param activityType}.
*/
void getStackBounds(int windowingMode, int activityType, Rect outBounds);
/**
- * Notifies window manager that {@link #isShowingDreamLw} has changed.
- */
- void notifyShowingDreamChanged();
-
- /**
* @return The currently active input method window.
*/
WindowState getInputMethodWindowLw();
@@ -648,7 +625,15 @@
*/
void onKeyguardShowingAndNotOccludedChanged();
- DisplayContentInfo getDefaultDisplayContentInfo();
+ /**
+ * Notifies window manager that power key is being pressed.
+ */
+ void onPowerKeyDown(boolean isScreenOn);
+
+ /**
+ * Notifies window manager that user is switched.
+ */
+ void onUserSwitched();
}
/**
@@ -735,17 +720,6 @@
public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs);
/**
- * Sanitize the layout parameters coming from a client. Allows the policy
- * to do things like ensure that windows of a specific type can't take
- * input focus.
- *
- * @param attrs The window layout parameters to be modified. These values
- * are modified in-place.
- */
- public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
- boolean hasStatusBarServicePermission);
-
- /**
* After the window manager has computed the current configuration based
* on its knowledge of the display and input devices, it gives the policy
* a chance to adjust the information contained in it. If you want to
@@ -941,40 +915,6 @@
public int getMaxWallpaperLayer();
/**
- * Return the display width available after excluding any screen
- * decorations that could never be removed in Honeycomb. That is, system bar or
- * button bar.
- */
- public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation,
- int uiMode, int displayId, DisplayCutout displayCutout);
-
- /**
- * Return the display height available after excluding any screen
- * decorations that could never be removed in Honeycomb. That is, system bar or
- * button bar.
- */
- public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation,
- int uiMode, int displayId, DisplayCutout displayCutout);
-
- /**
- * Return the available screen width that we should report for the
- * configuration. This must be no larger than
- * {@link #getNonDecorDisplayWidth(int, int, int, int, int, DisplayCutout)}; it may be smaller
- * than that to account for more transient decoration like a status bar.
- */
- public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation,
- int uiMode, int displayId, DisplayCutout displayCutout);
-
- /**
- * Return the available screen height that we should report for the
- * configuration. This must be no larger than
- * {@link #getNonDecorDisplayHeight(int, int, int, int, int, DisplayCutout)}; it may be smaller
- * than that to account for more transient decoration like a status bar.
- */
- public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation,
- int uiMode, int displayId, DisplayCutout displayCutout);
-
- /**
* Return whether the given window can become the Keyguard window. Typically returns true for
* the StatusBar.
*/
@@ -1013,65 +953,11 @@
int logo, int windowFlags, Configuration overrideConfig, int displayId);
/**
- * Prepare for a window being added to the window manager. You can throw an
- * exception here to prevent the window being added, or do whatever setup
- * you need to keep track of the window.
+ * Set or clear a window which can behave as the keyguard.
*
- * @param win The window being added.
- * @param attrs The window's LayoutParams.
- *
- * @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed, else an
- * error code to abort the add.
+ * @param win The window which can behave as the keyguard.
*/
- public int prepareAddWindowLw(WindowState win,
- WindowManager.LayoutParams attrs);
-
- /**
- * Called when a window is being removed from a window manager. Must not
- * throw an exception -- clean up as much as possible.
- *
- * @param win The window being removed.
- */
- public void removeWindowLw(WindowState win);
-
- /**
- * 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
- * to use the default animation.
- *
- * @param win The window that is changing.
- * @param transit What is happening to the window: {@link #TRANSIT_ENTER},
- * {@link #TRANSIT_EXIT}, {@link #TRANSIT_SHOW}, or
- * {@link #TRANSIT_HIDE}.
- *
- * @return Resource ID of the actual animation to use, or 0 for none.
- */
- public int selectAnimationLw(WindowState win, int transit);
-
- /**
- * Determine the animation to run for a rotation transition based on the
- * top fullscreen windows {@link WindowManager.LayoutParams#rotationAnimation}
- * and whether it is currently fullscreen and frontmost.
- *
- * @param anim The exiting animation resource id is stored in anim[0], the
- * entering animation resource id is stored in anim[1].
- */
- public void selectRotationAnimationLw(int anim[]);
-
- /**
- * Validate whether the current top fullscreen has specified the same
- * {@link WindowManager.LayoutParams#rotationAnimation} value as that
- * being passed in from the previous top fullscreen window.
- *
- * @param exitAnimId exiting resource id from the previous window.
- * @param enterAnimId entering resource id from the previous window.
- * @param forceDefault For rotation animations only, if true ignore the
- * animation values and just return false.
- * @return true if the previous values are still valid, false if they
- * should be replaced with the default.
- */
- public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId,
- boolean forceDefault);
+ void setKeyguardCandidateLw(@Nullable WindowState win);
/**
* Create and return an animation to re-display a window that was force hidden by Keyguard.
@@ -1148,100 +1034,21 @@
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags);
/**
- * Called when layout of the windows is about to start.
+ * Apply the keyguard policy to a specific window.
*
- * @param displayFrames frames of the display we are doing layout on.
- * @param uiMode The current uiMode in configuration.
+ * @param win The window to apply the keyguard policy.
+ * @param imeTarget The current IME target window.
*/
- default void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {}
+ void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget);
/**
- * Returns the bottom-most layer of the system decor, above which no policy decor should
- * be applied.
- */
- public int getSystemDecorLayerLw();
-
- /**
- * Called for each window attached to the window manager as layout is proceeding. The
- * implementation of this function must take care of setting the window's frame, either here or
- * in finishLayout().
+ * Called when the state of allow-lockscreen-when-on of the display is changed. See
+ * {@link WindowManager.LayoutParams#FLAG_ALLOW_LOCK_WHILE_SCREEN_ON}
*
- * @param win The window being positioned.
- * @param attached For sub-windows, the window it is attached to; this
- * window will already have had layoutWindow() called on it
- * so you can use its Rect. Otherwise null.
- * @param displayFrames The display frames.
+ * @param displayId The ID of the display.
+ * @param allow Whether the display allows showing lockscreen when it is on.
*/
- default void layoutWindowLw(
- WindowState win, WindowState attached, DisplayFrames displayFrames) {}
-
- /**
- * Return the layout hints for a newly added window. These values are computed on the
- * most recent layout, so they are not guaranteed to be correct.
- *
- * @param attrs The LayoutParams of the window.
- * @param taskBounds The bounds of the task this window is on or {@code null} if no task is
- * associated with the window.
- * @param displayFrames display frames.
- * @param floatingStack Whether the window's stack is floating.
- * @param outFrame The frame of the window.
- * @param outContentInsets The areas covered by system windows, expressed as positive insets.
- * @param outStableInsets The areas covered by stable system windows irrespective of their
- * current visibility. Expressed as positive insets.
- * @param outOutsets The areas that are not real display, but we would like to treat as such.
- * @param outDisplayCutout The area that has been cut away from the display.
- * @return Whether to always consume the navigation bar.
- * See {@link #isNavBarForcedShownLw(WindowState)}.
- */
- default boolean getLayoutHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
- DisplayFrames displayFrames, boolean floatingStack,
- Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
- DisplayCutout.ParcelableWrapper outDisplayCutout) {
- return false;
- }
-
- /**
- * Called following layout of all windows before each window has policy applied.
- *
- * @param displayWidth The current full width of the screen.
- * @param displayHeight The current full height of the screen.
- */
- public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight);
-
- /**
- * Called following layout of all window to apply policy to each window.
- *
- * @param win The window being positioned.
- * @param attrs The LayoutParams of the window.
- * @param attached For sub-windows, the window it is attached to. Otherwise null.
- */
- public void applyPostLayoutPolicyLw(WindowState win,
- WindowManager.LayoutParams attrs, WindowState attached, WindowState imeTarget);
-
- /**
- * Called following layout of all windows and after policy has been applied
- * to each window. If in this function you do
- * something that may have modified the animation state of another window,
- * be sure to return non-zero in order to perform another pass through layout.
- *
- * @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT},
- * {@link #FINISH_LAYOUT_REDO_CONFIG}, {@link #FINISH_LAYOUT_REDO_WALLPAPER},
- * or {@link #FINISH_LAYOUT_REDO_ANIM}.
- */
- public int finishPostLayoutPolicyLw();
-
- /**
- * Return true if it is okay to perform animations for an app transition
- * that is about to occur. You may return false for this if, for example,
- * the dream window is currently displayed so the switch should happen
- * immediately.
- */
- public boolean allowAppAnimationsLw();
-
- /**
- * A new window has been focused.
- */
- public int focusChangedLw(WindowState lastFocus, WindowState newFocus);
+ void setAllowLockscreenWhenOn(int displayId, boolean allow);
/**
* Called when the device has started waking up.
@@ -1430,8 +1237,6 @@
*/
public boolean isKeyguardDrawnLw();
- public boolean isShowingDreamLw();
-
/**
* Called when the system is mostly done booting to set whether
* the system should go into safe mode.
@@ -1491,14 +1296,6 @@
public void keepScreenOnStoppedLw();
/**
- * Called when a new system UI visibility is being reported, allowing
- * the policy to adjust what is actually reported.
- * @param visibility The raw visibility reported by the status bar.
- * @return The new desired visibility.
- */
- public int adjustSystemUiVisibilityLw(int visibility);
-
- /**
* Called by System UI to notify of changes to the visibility of Recents.
*/
public void setRecentsVisibilityLw(boolean visible);
@@ -1548,6 +1345,16 @@
public void showGlobalActions();
/**
+ * Returns whether the user setup is complete.
+ */
+ boolean isUserSetupComplete();
+
+ /**
+ * Returns the current UI mode.
+ */
+ int getUiMode();
+
+ /**
* Called when the current user changes. Guaranteed to be called before the broadcast
* of the new user id is made to all listeners.
*
@@ -1602,69 +1409,6 @@
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
/**
- * Calculates the stable insets without running a layout.
- *
- * @param displayRotation the current display rotation
- * @param displayWidth the current display width
- * @param displayHeight the current display height
- * @param displayCutout the current display cutout
- * @param outInsets the insets to return
- */
- public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
- DisplayCutout displayCutout, Rect outInsets);
-
-
- /**
- * @return true if the navigation bar is forced to stay visible
- */
- public boolean isNavBarForcedShownLw(WindowState win);
-
- /**
- * @return The side of the screen where navigation bar is positioned.
- * @see #NAV_BAR_LEFT
- * @see #NAV_BAR_RIGHT
- * @see #NAV_BAR_BOTTOM
- */
- @NavigationBarPosition
- int getNavBarPosition();
-
- /**
- * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
- * bar or button bar. See {@link #getNonDecorDisplayWidth}.
- *
- * @param displayRotation the current display rotation
- * @param displayWidth the current display width
- * @param displayHeight the current display height
- * @param displayCutout the current display cutout
- * @param outInsets the insets to return
- */
- public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
- DisplayCutout displayCutout, Rect outInsets);
-
- /**
- * @param displayRotation the current display rotation
- * @param displayWidth the current display width
- * @param displayHeight the current display height
- * @param dockSide the dockside asking if allowed
- * @param originalDockSide the side that was original docked to in split screen
- * @return True if a specified {@param dockSide} is allowed on the current device, or false
- * otherwise. It is guaranteed that at least one dock side for a particular orientation
- * is allowed, so for example, if DOCKED_RIGHT is not allowed, DOCKED_LEFT is allowed.
- * If navigation bar is movable then the docked side would bias towards the
- * {@param originalDockSide}.
- */
- public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
- int displayHeight, int displayRotation);
-
- /**
- * Called when the configuration has changed, and it's safe to load new values from resources.
- */
- public void onConfigurationChanged(DisplayContentInfo displayContentInfo);
-
- public boolean shouldRotateSeamlessly(DisplayRotation displayRotation,
- int oldRotation, int newRotation);
-
- /**
* Called when System UI has been started.
*/
void onSystemUiStarted();
@@ -1697,17 +1441,6 @@
public void requestUserActivityNotification();
/**
- * Called when the state of lock task mode changes. This should be used to disable immersive
- * mode confirmation.
- *
- * @param lockTaskState the new lock task mode state. One of
- * {@link ActivityManager#LOCK_TASK_MODE_NONE},
- * {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
- * {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
- */
- void onLockTaskStateChangedLw(int lockTaskState);
-
- /**
* Updates the flag about whether AOD is showing.
*
* @return whether the value was changed.
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 812fd82..79e2688 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.power;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.thermal.V1_0.ThermalStatus;
import android.hardware.thermal.V1_0.ThermalStatusCode;
@@ -26,12 +27,16 @@
import android.os.HwBinder;
import android.os.IThermalEventListener;
import android.os.IThermalService;
+import android.os.IThermalStatusListener;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.Temperature;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.SystemService;
@@ -39,6 +44,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;
@@ -51,210 +57,154 @@
public class ThermalManagerService extends SystemService {
private static final String TAG = ThermalManagerService.class.getSimpleName();
- /** Registered observers of the thermal changed events. Cookie is used to store type */
+ /** Lock to protect listen list. */
+ private final Object mLock = new Object();
+
+ /**
+ * Registered observers of the thermal events. Cookie is used to store type as Integer, null
+ * means no filter.
+ */
@GuardedBy("mLock")
private final RemoteCallbackList<IThermalEventListener> mThermalEventListeners =
new RemoteCallbackList<>();
- /** Lock to protect HAL handles and listen list. */
- private final Object mLock = new Object();
-
- /** Newly registered callback. */
+ /** Registered observers of the thermal status. */
@GuardedBy("mLock")
- private IThermalEventListener mNewListenerCallback = null;
+ private final RemoteCallbackList<IThermalStatusListener> mThermalStatusListeners =
+ new RemoteCallbackList<>();
- /** Newly registered callback type, null means not filter type. */
+ /** Current thermal status */
@GuardedBy("mLock")
- private Integer mNewListenerType = null;
+ private int mStatus;
+
+ /** Current thermal map, key as name */
+ @GuardedBy("mLock")
+ private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
/** Local PMS handle. */
private final PowerManager mPowerManager;
- /** Proxy object for the Thermal HAL 2.0 service. */
+ /** HAL wrapper. */
+ private ThermalHalWrapper mHalWrapper;
+
+ /** Hal ready. */
@GuardedBy("mLock")
- private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
+ private boolean mHalReady;
- /** Proxy object for the Thermal HAL 1.1 service. */
- @GuardedBy("mLock")
- private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
-
- /** Cookie for matching the right end point. */
- private static final int THERMAL_HAL_DEATH_COOKIE = 5612;
-
- /** HWbinder callback for Thermal HAL 2.0. */
- private final IThermalChangedCallback.Stub mThermalCallback20 =
- new IThermalChangedCallback.Stub() {
- @Override
- public void notifyThrottling(
- android.hardware.thermal.V2_0.Temperature temperature) {
- android.os.Temperature thermalSvcTemp = new android.os.Temperature(
- temperature.value, temperature.type, temperature.name,
- temperature.throttlingStatus);
- final long token = Binder.clearCallingIdentity();
- try {
- notifyThrottlingImpl(thermalSvcTemp);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- };
-
- /** HWbinder callback for Thermal HAL 1.1. */
- private final IThermalCallback.Stub mThermalCallback11 =
- new IThermalCallback.Stub() {
- @Override
- public void notifyThrottling(boolean isThrottling,
- android.hardware.thermal.V1_0.Temperature temperature) {
- android.os.Temperature thermalSvcTemp = new android.os.Temperature(
- temperature.currentValue, temperature.type, temperature.name,
- isThrottling ? ThrottlingSeverity.SEVERE : ThrottlingSeverity.NONE);
- final long token = Binder.clearCallingIdentity();
- try {
- notifyThrottlingImpl(thermalSvcTemp);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- };
+ /** Invalid throttling status */
+ private static final int INVALID_THROTTLING = Integer.MIN_VALUE;
public ThermalManagerService(Context context) {
+ this(context, null);
+ }
+
+ @VisibleForTesting
+ ThermalManagerService(Context context, @Nullable ThermalHalWrapper halWrapper) {
super(context);
mPowerManager = context.getSystemService(PowerManager.class);
+ mHalWrapper = halWrapper;
+ // Initialize to invalid to send status onActivityManagerReady
+ mStatus = INVALID_THROTTLING;
}
- private void setNewListener(IThermalEventListener listener, Integer type) {
- synchronized (mLock) {
- mNewListenerCallback = listener;
- mNewListenerType = type;
+ @Override
+ public void onStart() {
+ publishBinderService(Context.THERMAL_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ onActivityManagerReady();
}
}
- private void clearNewListener() {
+ private void onActivityManagerReady() {
synchronized (mLock) {
- mNewListenerCallback = null;
- mNewListenerType = null;
- }
- }
-
- private final IThermalService.Stub mService = new IThermalService.Stub() {
- @Override
- public void registerThermalEventListener(IThermalEventListener listener) {
- synchronized (mLock) {
- mThermalEventListeners.register(listener, null);
- // Notify its callback after new client registered.
- setNewListener(listener, null);
- long token = Binder.clearCallingIdentity();
- try {
- notifyCurrentTemperaturesLocked();
- } finally {
- Binder.restoreCallingIdentity(token);
- clearNewListener();
+ // Connect to HAL and post to listeners.
+ boolean halConnected = (mHalWrapper != null);
+ if (!halConnected) {
+ mHalWrapper = new ThermalHal20Wrapper();
+ halConnected = mHalWrapper.connectToHal();
+ if (!halConnected) {
+ mHalWrapper = new ThermalHal11Wrapper();
+ halConnected = mHalWrapper.connectToHal();
}
}
- }
-
- @Override
- public void registerThermalEventListenerWithType(IThermalEventListener listener, int type) {
- synchronized (mLock) {
- mThermalEventListeners.register(listener, new Integer(type));
- setNewListener(listener, new Integer(type));
- // Notify its callback after new client registered.
- long token = Binder.clearCallingIdentity();
- try {
- notifyCurrentTemperaturesLocked();
- } finally {
- Binder.restoreCallingIdentity(token);
- clearNewListener();
- }
+ mHalWrapper.setCallback(this::onTemperatureChangedCallback);
+ if (!halConnected) {
+ return;
}
- }
-
- @Override
- public void unregisterThermalEventListener(IThermalEventListener listener) {
- synchronized (mLock) {
- long token = Binder.clearCallingIdentity();
- try {
- mThermalEventListeners.unregister(listener);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(false,
+ 0);
+ final int count = temperatures.size();
+ for (int i = 0; i < count; i++) {
+ onTemperatureChanged(temperatures.get(i), false);
}
+ onTemperatureMapChangedLocked();
+ mHalReady = halConnected /* true */;
}
-
- @Override
- public List<android.os.Temperature> getCurrentTemperatures() {
- List<android.os.Temperature> ret;
- long token = Binder.clearCallingIdentity();
- try {
- ret = getCurrentTemperaturesInternal(false, 0 /* not used */);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return ret;
- }
-
- @Override
- public List<android.os.Temperature> getCurrentTemperaturesWithType(int type) {
- List<android.os.Temperature> ret;
- long token = Binder.clearCallingIdentity();
- try {
- ret = getCurrentTemperaturesInternal(true, type);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return ret;
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
- pw.println("ThermalEventListeners dump:");
- synchronized (mLock) {
- mThermalEventListeners.dump(pw, "\t");
- pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes" : "no"));
- pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes" : "no"));
- }
- }
- };
-
- private List<android.os.Temperature> getCurrentTemperaturesInternal(boolean shouldFilter,
- int type) {
- List<android.os.Temperature> ret = new ArrayList<>();
- synchronized (mLock) {
- if (mThermalHal20 == null) {
- return ret;
- }
- try {
- mThermalHal20.getCurrentTemperatures(shouldFilter, type,
- (ThermalStatus status,
- ArrayList<android.hardware.thermal.V2_0.Temperature>
- temperatures) -> {
- if (ThermalStatusCode.SUCCESS == status.code) {
- for (android.hardware.thermal.V2_0.Temperature
- temperature : temperatures) {
- ret.add(new android.os.Temperature(
- temperature.value, temperature.type, temperature.name,
- temperature.throttlingStatus));
- }
- } else {
- Slog.e(TAG,
- "Couldn't get temperatures because of HAL error: "
- + status.debugMessage);
- }
-
- });
- } catch (RemoteException e) {
- Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
- connectToHalLocked();
- // Post to listeners after reconnect to HAL.
- notifyCurrentTemperaturesLocked();
- }
- }
- return ret;
}
- private void notifyListener(android.os.Temperature temperature, IThermalEventListener listener,
- Integer type) {
+ private void postStatusListener(IThermalStatusListener listener) {
+ final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
+ try {
+ listener.onStatusChange(mStatus);
+ } catch (RemoteException | RuntimeException e) {
+ Slog.e(TAG, "Thermal callback failed to call", e);
+ }
+ });
+ if (!thermalCallbackQueued) {
+ Slog.e(TAG, "Thermal callback failed to queue");
+ }
+ }
+
+ private void notifyStatusListenersLocked() {
+ if (!Temperature.isValidStatus(mStatus)) {
+ return;
+ }
+ final int length = mThermalStatusListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ final IThermalStatusListener listener =
+ mThermalStatusListeners.getBroadcastItem(i);
+ postStatusListener(listener);
+ }
+ } finally {
+ mThermalStatusListeners.finishBroadcast();
+ }
+ }
+
+ private void onTemperatureMapChangedLocked() {
+ int newStatus = INVALID_THROTTLING;
+ final int count = mTemperatureMap.size();
+ for (int i = 0; i < count; i++) {
+ Temperature t = mTemperatureMap.valueAt(i);
+ if (t.getStatus() >= newStatus) {
+ newStatus = t.getStatus();
+ }
+ }
+ if (newStatus != mStatus) {
+ mStatus = newStatus;
+ notifyStatusListenersLocked();
+ }
+ }
+
+
+ private void postEventListenerCurrentTemperatures(IThermalEventListener listener,
+ @Nullable Integer type) {
+ synchronized (mLock) {
+ final int count = mTemperatureMap.size();
+ for (int i = 0; i < count; i++) {
+ postEventListener(mTemperatureMap.valueAt(i), listener,
+ type);
+ }
+ }
+ }
+
+ private void postEventListener(Temperature temperature,
+ IThermalEventListener listener,
+ @Nullable Integer type) {
// Skip if listener registered with a different type
if (type != null && type != temperature.getType()) {
return;
@@ -271,11 +221,26 @@
}
}
- private void notifyThrottlingImpl(android.os.Temperature temperature) {
+ private void notifyEventListenersLocked(Temperature temperature) {
+ final int length = mThermalEventListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ final IThermalEventListener listener =
+ mThermalEventListeners.getBroadcastItem(i);
+ final Integer type =
+ (Integer) mThermalEventListeners.getBroadcastCookie(i);
+ postEventListener(temperature, listener, type);
+ }
+ } finally {
+ mThermalEventListeners.finishBroadcast();
+ }
+ }
+
+ private void onTemperatureChanged(Temperature temperature, boolean sendStatus) {
synchronized (mLock) {
// Thermal Shutdown for Skin temperature
- if (temperature.getStatus() == android.os.Temperature.THROTTLING_SHUTDOWN
- && temperature.getType() == android.os.Temperature.TYPE_SKIN) {
+ 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);
@@ -284,107 +249,420 @@
}
}
- if (mNewListenerCallback != null) {
- // Only notify current newly added callback.
- notifyListener(temperature, mNewListenerCallback, mNewListenerType);
+ Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
+ if (old != null) {
+ if (old.getStatus() != temperature.getStatus()) {
+ notifyEventListenersLocked(temperature);
+ }
} else {
- final int length = mThermalEventListeners.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- final IThermalEventListener listener =
- mThermalEventListeners.getBroadcastItem(i);
- final Integer type = (Integer) mThermalEventListeners.getBroadcastCookie(i);
- notifyListener(temperature, listener, type);
- }
- } finally {
- mThermalEventListeners.finishBroadcast();
- }
+ notifyEventListenersLocked(temperature);
+ }
+ if (sendStatus) {
+ onTemperatureMapChangedLocked();
}
}
}
- @Override
- public void onStart() {
- publishBinderService(Context.THERMAL_SERVICE, mService);
+ private void onTemperatureChangedCallback(Temperature temperature) {
+ onTemperatureChanged(temperature, true);
}
- @Override
- public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- onActivityManagerReady();
+ private void dumpTemperaturesLocked(PrintWriter pw, String prefix,
+ Collection<Temperature> temperatures) {
+ for (Temperature t : temperatures) {
+ pw.print(prefix);
+ String out = String.format("Name: %s, Type: %d, Status: %d, Value: %f",
+ t.getName(),
+ t.getType(),
+ t.getStatus(),
+ t.getValue()
+ );
+ pw.println(out);
}
}
- private void notifyCurrentTemperaturesCallbackLocked(ThermalStatus status,
- ArrayList<android.hardware.thermal.V2_0.Temperature> temperatures) {
- if (ThermalStatusCode.SUCCESS != status.code) {
- Slog.e(TAG, "Couldn't get temperatures because of HAL error: "
- + status.debugMessage);
- return;
- }
- for (android.hardware.thermal.V2_0.Temperature temperature : temperatures) {
- android.os.Temperature thermal_svc_temp =
- new android.os.Temperature(
- temperature.value, temperature.type,
- temperature.name,
- temperature.throttlingStatus);
- notifyThrottlingImpl(thermal_svc_temp);
- }
- }
-
- private void notifyCurrentTemperaturesLocked() {
- if (mThermalHal20 == null) {
- return;
- }
- try {
- mThermalHal20.getCurrentTemperatures(false, 0,
- this::notifyCurrentTemperaturesCallbackLocked);
- } catch (RemoteException e) {
- Slog.e(TAG, "Couldn't get temperatures, reconnecting...", e);
- connectToHalLocked();
- }
- }
-
- private void onActivityManagerReady() {
- synchronized (mLock) {
- connectToHalLocked();
- // Post to listeners after connect to HAL.
- notifyCurrentTemperaturesLocked();
- }
- }
-
- final class DeathRecipient implements HwBinder.DeathRecipient {
+ @VisibleForTesting
+ final IThermalService.Stub mService = new IThermalService.Stub() {
@Override
- public void serviceDied(long cookie) {
- if (cookie == THERMAL_HAL_DEATH_COOKIE) {
- Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
+ public boolean registerThermalEventListener(IThermalEventListener listener) {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mThermalEventListeners.register(listener, null)) {
+ return false;
+ }
+ if (mHalReady) {
+ // Notify its callback after new client registered.
+ postEventListenerCurrentTemperatures(listener, null);
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public boolean registerThermalEventListenerWithType(IThermalEventListener listener,
+ int type) {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mThermalEventListeners.register(listener, new Integer(type))) {
+ return false;
+ }
+ if (mHalReady) {
+ // Notify its callback after new client registered.
+ postEventListenerCurrentTemperatures(listener, new Integer(type));
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public boolean unregisterThermalEventListener(IThermalEventListener listener) {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mThermalEventListeners.unregister(listener);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public List<Temperature> getCurrentTemperatures() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mHalReady) {
+ return new ArrayList<>();
+ }
+ return mHalWrapper.getCurrentTemperatures(false, 0 /* not used */);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public List<Temperature> getCurrentTemperaturesWithType(int type) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mHalReady) {
+ return new ArrayList<>();
+ }
+ return mHalWrapper.getCurrentTemperatures(true, type);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean registerThermalStatusListener(IThermalStatusListener listener) {
+ synchronized (mLock) {
+ // Notify its callback after new client registered.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mThermalStatusListeners.register(listener)) {
+ return false;
+ }
+ if (mHalReady) {
+ // Notify its callback after new client registered.
+ postStatusListener(listener);
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public boolean unregisterThermalStatusListener(IThermalStatusListener listener) {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mThermalStatusListeners.unregister(listener);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public int getCurrentStatus() {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return Temperature.isValidStatus(mStatus) ? mStatus
+ : Temperature.THROTTLING_NONE;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
synchronized (mLock) {
- connectToHalLocked();
- // Post to listeners after reconnect to HAL.
- notifyCurrentTemperaturesLocked();
+ pw.println("ThermalEventListeners:");
+ mThermalEventListeners.dump(pw, "\t");
+ pw.println("ThermalStatusListeners:");
+ mThermalStatusListeners.dump(pw, "\t");
+ pw.println("Thermal Status: " + Integer.toString(mStatus));
+ pw.println("Cached temperatures:");
+ dumpTemperaturesLocked(pw, "\t", mTemperatureMap.values());
+ pw.println("HAL Ready: " + Boolean.toString(mHalReady));
+ if (mHalReady) {
+ pw.println("HAL connection:");
+ mHalWrapper.dump(pw, "\t");
+ pw.println("Current temperatures from HAL:");
+ dumpTemperaturesLocked(pw, "\t",
+ mHalWrapper.getCurrentTemperatures(false, 0));
+ }
+ }
+
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ abstract static class ThermalHalWrapper {
+ protected static final String TAG = ThermalHalWrapper.class.getSimpleName();
+
+ /** Lock to protect HAL handle. */
+ protected final Object mHalLock = new Object();
+
+ @FunctionalInterface
+ interface TemperatureChangedCallback {
+ void onValues(Temperature temperature);
+ }
+
+ /** Temperature callback. */
+ protected TemperatureChangedCallback mCallback;
+
+ /** Cookie for matching the right end point. */
+ protected static final int THERMAL_HAL_DEATH_COOKIE = 5612;
+
+ @VisibleForTesting
+ protected void setCallback(TemperatureChangedCallback cb) {
+ mCallback = cb;
+ }
+
+ protected abstract List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+ int type);
+
+ protected abstract boolean connectToHal();
+
+ protected abstract void dump(PrintWriter pw, String prefix);
+
+ protected void resendCurrentTemperatures() {
+ synchronized (mHalLock) {
+ List<Temperature> temperatures = getCurrentTemperatures(false, 0);
+ final int count = temperatures.size();
+ for (int i = 0; i < count; i++) {
+ mCallback.onValues(temperatures.get(i));
+ }
+ }
+ }
+
+ final class DeathRecipient implements HwBinder.DeathRecipient {
+ @Override
+ public void serviceDied(long cookie) {
+ if (cookie == THERMAL_HAL_DEATH_COOKIE) {
+ Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
+ synchronized (mHalLock) {
+ connectToHal();
+ // Post to listeners after reconnect to HAL.
+ resendCurrentTemperatures();
+ }
}
}
}
}
- private void connectToHalLocked() {
- try {
- mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
- mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
- mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
- 0 /* not used */);
- } catch (NoSuchElementException | RemoteException e) {
- Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1.");
- mThermalHal20 = null;
- try {
- mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
- mThermalHal11.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
- mThermalHal11.registerThermalCallback(mThermalCallback11);
- } catch (NoSuchElementException | RemoteException e2) {
- Slog.e(TAG,
- "Thermal HAL 1.1 service not connected, no thermal call back "
- + "will be called.");
- mThermalHal11 = null;
+ static class ThermalHal11Wrapper extends ThermalHalWrapper {
+ /** Proxy object for the Thermal HAL 1.1 service. */
+ @GuardedBy("mHalLock")
+ private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
+
+ /** HWbinder callback for Thermal HAL 1.1. */
+ private final IThermalCallback.Stub mThermalCallback11 =
+ new IThermalCallback.Stub() {
+ @Override
+ public void notifyThrottling(boolean isThrottling,
+ android.hardware.thermal.V1_0.Temperature temperature) {
+ Temperature thermalSvcTemp = new Temperature(
+ temperature.currentValue, temperature.type, temperature.name,
+ isThrottling ? ThrottlingSeverity.SEVERE
+ : ThrottlingSeverity.NONE);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCallback.onValues(thermalSvcTemp);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ @Override
+ protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+ int type) {
+ synchronized (mHalLock) {
+ List<Temperature> ret = new ArrayList<>();
+ if (mThermalHal11 == null) {
+ return ret;
+ }
+ try {
+ mThermalHal11.getTemperatures(
+ (ThermalStatus status,
+ ArrayList<android.hardware.thermal.V1_0.Temperature>
+ temperatures) -> {
+ if (ThermalStatusCode.SUCCESS == status.code) {
+ for (android.hardware.thermal.V1_0.Temperature
+ temperature : temperatures) {
+ if (shouldFilter && type != temperature.type) {
+ continue;
+ }
+ // Thermal HAL 1.1 doesn't report current throttling status
+ ret.add(new Temperature(
+ temperature.currentValue, temperature.type,
+ temperature.name,
+ Temperature.THROTTLING_NONE));
+ }
+ } else {
+ Slog.e(TAG,
+ "Couldn't get temperatures because of HAL error: "
+ + status.debugMessage);
+ }
+
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
+ connectToHal();
+ }
+ return ret;
+ }
+ }
+
+ @Override
+ protected boolean connectToHal() {
+ synchronized (mHalLock) {
+ try {
+ mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
+ mThermalHal11.linkToDeath(new DeathRecipient(),
+ THERMAL_HAL_DEATH_COOKIE);
+ mThermalHal11.registerThermalCallback(mThermalCallback11);
+ } catch (NoSuchElementException | RemoteException e) {
+ Slog.e(TAG,
+ "Thermal HAL 1.1 service not connected, no thermal call back will be "
+ + "called.");
+ mThermalHal11 = null;
+ }
+ return (mThermalHal11 != null);
+ }
+ }
+
+ @Override
+ protected void dump(PrintWriter pw, String prefix) {
+ synchronized (mHalLock) {
+ pw.print(prefix);
+ pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes"
+ : "no"));
+ }
+ }
+ }
+
+ static class ThermalHal20Wrapper extends ThermalHalWrapper {
+ /** Proxy object for the Thermal HAL 2.0 service. */
+ @GuardedBy("mHalLock")
+ private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
+
+ /** HWbinder callback for Thermal HAL 2.0. */
+ private final IThermalChangedCallback.Stub mThermalCallback20 =
+ new IThermalChangedCallback.Stub() {
+ @Override
+ public void notifyThrottling(
+ android.hardware.thermal.V2_0.Temperature temperature) {
+ Temperature thermalSvcTemp = new Temperature(
+ temperature.value, temperature.type, temperature.name,
+ temperature.throttlingStatus);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCallback.onValues(thermalSvcTemp);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ @Override
+ protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+ int type) {
+ synchronized (mHalLock) {
+ List<Temperature> ret = new ArrayList<>();
+ if (mThermalHal20 == null) {
+ return ret;
+ }
+ try {
+ mThermalHal20.getCurrentTemperatures(shouldFilter, type,
+ (ThermalStatus status,
+ ArrayList<android.hardware.thermal.V2_0.Temperature>
+ temperatures) -> {
+ if (ThermalStatusCode.SUCCESS == status.code) {
+ for (android.hardware.thermal.V2_0.Temperature
+ temperature : temperatures) {
+ ret.add(new Temperature(
+ temperature.value, temperature.type,
+ temperature.name,
+ temperature.throttlingStatus));
+ }
+ } else {
+ Slog.e(TAG,
+ "Couldn't get temperatures because of HAL error: "
+ + status.debugMessage);
+ }
+
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
+ connectToHal();
+ }
+ return ret;
+ }
+ }
+
+ @Override
+ protected boolean connectToHal() {
+ synchronized (mHalLock) {
+ try {
+ mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
+ mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
+ mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
+ 0 /* not used */);
+ } catch (NoSuchElementException | RemoteException e) {
+ Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1.");
+ mThermalHal20 = null;
+ }
+ return (mThermalHal20 != null);
+ }
+ }
+
+ @Override
+ protected void dump(PrintWriter pw, String prefix) {
+ synchronized (mHalLock) {
+ pw.print(prefix);
+ pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes"
+ : "no"));
}
}
}
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
index 538f4cf..cb89780 100644
--- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -48,6 +48,7 @@
static final boolean DEBUG = false;
private static final String LOG_TAG = RemoteRoleControllerService.class.getSimpleName();
+ // TODO: STOPSHIP: This isn't the right thread, as we are also using it to write to disk.
@NonNull
private static final Handler sCallbackHandler = BackgroundThread.getHandler();
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index d01e762..b390eeb 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -30,6 +30,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.text.TextUtils;
@@ -42,6 +44,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -284,12 +287,26 @@
}
@Override
+ public void setRoleNamesFromController(@NonNull List<String> roleNames) {
+ Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
+ getContext().enforceCallingOrSelfPermission(
+ RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
+ "setRoleNamesFromController");
+
+ int userId = UserHandle.getCallingUserId();
+ synchronized (mLock) {
+ RoleUserState userState = getUserStateLocked(userId);
+ userState.setRoleNamesLocked(roleNames);
+ }
+ }
+
+ @Override
public boolean addRoleHolderFromController(@NonNull String roleName,
@NonNull String packageName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
getContext().enforceCallingOrSelfPermission(
- RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER,
+ RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
"addRoleHolderFromController");
int userId = UserHandle.getCallingUserId();
@@ -305,7 +322,7 @@
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
getContext().enforceCallingOrSelfPermission(
- RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER,
+ RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
"removeRoleHolderFromController");
int userId = UserHandle.getCallingUserId();
@@ -320,5 +337,13 @@
return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false, true, name, null);
}
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ 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..e1977ef
--- /dev/null
+++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
@@ -0,0 +1,134 @@
+/*
+ * 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.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 {
+ private final IRoleManager mRoleManager;
+
+ RoleManagerShellCommand(IRoleManager roleManager) {
+ mRoleManager = roleManager;
+ }
+
+ private class Callback extends IRoleManagerCallback.Stub {
+ 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(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 68e737e..9c43f4d 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -45,6 +45,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.util.List;
/**
* Stores the state of roles for a user.
@@ -101,7 +102,11 @@
@GuardedBy("RoleManagerService.mLock")
public void setVersionLocked(int version) {
throwIfDestroyedLocked();
+ if (mVersion == version) {
+ return;
+ }
mVersion = version;
+ writeAsyncLocked();
}
/**
@@ -132,6 +137,41 @@
}
/**
+ * Set the names of all available roles.
+ *
+ * @param roleNames the names of all the available roles
+ */
+ @GuardedBy("RoleManagerService.mLock")
+ public void setRoleNamesLocked(@NonNull List<String> roleNames) {
+ throwIfDestroyedLocked();
+ boolean changed = false;
+ for (int i = mRoles.size() - 1; i >= 0; i--) {
+ String roleName = mRoles.keyAt(i);
+ if (!roleNames.contains(roleName)) {
+ ArraySet<String> packageNames = mRoles.valueAt(i);
+ if (!packageNames.isEmpty()) {
+ Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up, role: "
+ + roleName + ", holders: " + packageNames);
+ }
+ mRoles.removeAt(i);
+ changed = true;
+ }
+ }
+ int roleNamesSize = roleNames.size();
+ for (int i = 0; i < roleNamesSize; i++) {
+ String roleName = roleNames.get(i);
+ if (!mRoles.containsKey(roleName)) {
+ mRoles.put(roleName, new ArraySet<>());
+ Slog.i(LOG_TAG, "Added new role: " + roleName);
+ changed = true;
+ }
+ }
+ if (changed) {
+ writeAsyncLocked();
+ }
+ }
+
+ /**
* Add a holder to a role.
*
* @param roleName the name of the role to add the holder to
@@ -150,7 +190,10 @@
+ ", package: " + packageName);
return false;
}
- roleHolders.add(packageName);
+ boolean changed = roleHolders.add(packageName);
+ if (changed) {
+ writeAsyncLocked();
+ }
return true;
}
@@ -173,7 +216,10 @@
+ ", package: " + packageName);
return false;
}
- roleHolders.remove(packageName);
+ boolean changed = roleHolders.remove(packageName);
+ if (changed) {
+ writeAsyncLocked();
+ }
return true;
}
@@ -181,7 +227,7 @@
* Schedule writing the state to file.
*/
@GuardedBy("RoleManagerService.mLock")
- public void writeAsyncLocked() {
+ private void writeAsyncLocked() {
throwIfDestroyedLocked();
int version = mVersion;
ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
@@ -192,6 +238,7 @@
roles.put(roleName, roleHolders);
}
mWriteHandler.removeCallbacksAndMessages(null);
+ // TODO: Throttle writes.
mWriteHandler.sendMessage(PooledLambda.obtainMessage(
RoleUserState::writeSync, this, version, roles));
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index d2ca850..aa11e1e 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -51,6 +51,7 @@
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
@@ -194,8 +195,10 @@
"/system/bin/traced", // Perfetto.
"/system/bin/traced_probes", // Perfetto.
"webview_zygote",
- "zygote",
- "zygote64",
+ // Temporarily excluded zygote to investigate its forking consequences in
+ // NativeProcessMemoryState.
+ // "zygote",
+ // "zygote64",
};
@@ -1515,6 +1518,21 @@
pulledData.add(e);
}
+ private void pullBuildInformation(int tagId,
+ long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeString(Build.FINGERPRINT);
+ e.writeString(Build.BRAND);
+ e.writeString(Build.PRODUCT);
+ e.writeString(Build.DEVICE);
+ e.writeString(Build.VERSION.RELEASE);
+ e.writeString(Build.ID);
+ e.writeString(Build.VERSION.INCREMENTAL);
+ e.writeString(Build.TYPE);
+ e.writeString(Build.TAGS);
+ pulledData.add(e);
+ }
+
private BatteryStatsHelper getBatteryStatsHelper() {
if (mBatteryStatsHelper == null) {
final long callingToken = Binder.clearCallingIdentity();
@@ -1630,37 +1648,42 @@
if (this.mKernelCpuThreadReader == null) {
return;
}
- KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = this.mKernelCpuThreadReader
- .getCurrentProcessCpuUsage();
- if (processCpuUsage == null) {
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
+ this.mKernelCpuThreadReader.getProcessCpuUsageByUids();
+ if (processCpuUsages == null) {
return;
}
int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
- for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage
- : processCpuUsage.threadCpuUsages) {
- if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
- Slog.w(TAG, "Unexpected number of usage times,"
- + " expected " + cpuFrequencies.length
- + " but got " + threadCpuUsage.usageTimesMillis.length);
- continue;
- }
-
- for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) {
- // Do not report CPU usage at a frequency when it's zero
- if (threadCpuUsage.usageTimesMillis[i] == 0) {
+ for (int i = 0; i < processCpuUsages.size(); i++) {
+ KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
+ ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
+ processCpuUsage.threadCpuUsages;
+ for (int j = 0; j < threadCpuUsages.size(); j++) {
+ KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j);
+ if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
+ Slog.w(TAG, "Unexpected number of usage times,"
+ + " expected " + cpuFrequencies.length
+ + " but got " + threadCpuUsage.usageTimesMillis.length);
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);
- e.writeInt(cpuFrequencies[i]);
- e.writeInt(threadCpuUsage.usageTimesMillis[i]);
- pulledData.add(e);
+ 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);
+ e.writeInt(cpuFrequencies[k]);
+ e.writeInt(threadCpuUsage.usageTimesMillis[k]);
+ pulledData.add(e);
+ }
}
}
}
@@ -1803,6 +1826,10 @@
pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.BUILD_INFORMATION: {
+ pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
case StatsLog.PROCESS_CPU_TIME: {
pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 095eaa5..a66f0ca 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -44,36 +44,42 @@
*/
void showPictureInPictureMenu();
- void setWindowState(int window, int state);
+ void setWindowState(int displayId, int window, int state);
/**
* Notifies the status bar 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 which has this event.
*/
- void appTransitionPending();
+ void appTransitionPending(int displayId);
/**
* Notifies the status bar that a pending app transition has been cancelled.
+ *
+ * @param displayId the ID of the display which has this event.
*/
- void appTransitionCancelled();
+ void appTransitionCancelled(int displayId);
/**
* Notifies the status bar that an app transition is now being executed.
*
+ * @param displayId the ID of the display which has this event.
* @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);
void startAssist(Bundle args);
void onCameraLaunchGestureDetected(int source);
- void topAppWindowChanged(boolean menuVisible);
- void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
- Rect fullscreenBounds, Rect dockedBounds, String cause);
+ void topAppWindowChanged(int displayId, boolean menuVisible);
+ void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, Rect fullscreenBounds, Rect dockedBounds, String cause);
void toggleSplitScreen();
- void appTransitionFinished();
+ void appTransitionFinished(int displayId);
void toggleRecentApps();
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3e07ebe..0d66a2c8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -19,6 +19,7 @@
import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
import android.app.ActivityThread;
+import android.app.Notification;
import android.app.StatusBarManager;
import android.content.ComponentName;
import android.content.Context;
@@ -39,6 +40,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import android.view.Display;
import com.android.internal.R;
import com.android.internal.statusbar.IStatusBar;
@@ -237,14 +239,22 @@
}
@Override
- public void topAppWindowChanged(boolean menuVisible) {
+ 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);
}
@Override
- public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
- int mask,
- Rect fullscreenBounds, Rect dockedBounds, String cause) {
+ 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,
dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
}
@@ -259,8 +269,13 @@
}
}
- public void appTransitionFinished() {
+ @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();
@@ -358,7 +373,11 @@
}
@Override
- public void setWindowState(int window, int state) {
+ 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);
@@ -367,7 +386,11 @@
}
@Override
- public void appTransitionPending() {
+ 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();
@@ -376,7 +399,11 @@
}
@Override
- public void appTransitionCancelled() {
+ 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();
@@ -385,8 +412,12 @@
}
@Override
- public void appTransitionStarting(long statusBarAnimationsStartTime,
+ 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(
@@ -1050,14 +1081,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);
}
@@ -1147,12 +1180,14 @@
}
@Override
- public void onNotificationSmartReplySent(String key, int replyIndex)
+ public void onNotificationSmartReplySent(
+ String key, int replyIndex, CharSequence reply, boolean generatedByAssistant)
throws RemoteException {
enforceStatusBarService();
long identity = Binder.clearCallingIdentity();
try {
- mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex);
+ mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex, reply,
+ generatedByAssistant);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 5ce8145..c8a68b4 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -28,18 +28,22 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.service.textclassifier.IConversationActionsCallback;
import android.service.textclassifier.ITextClassificationCallback;
import android.service.textclassifier.ITextClassifierService;
+import android.service.textclassifier.ITextLanguageCallback;
import android.service.textclassifier.ITextLinksCallback;
import android.service.textclassifier.ITextSelectionCallback;
import android.service.textclassifier.TextClassifierService;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
+import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
@@ -210,6 +214,50 @@
}
@Override
+ public void onDetectLanguage(
+ TextClassificationSessionId sessionId,
+ TextLanguage.Request request,
+ ITextLanguageCallback callback) throws RemoteException {
+ Preconditions.checkNotNull(request);
+ Preconditions.checkNotNull(callback);
+
+ synchronized (mLock) {
+ UserState userState = getCallingUserStateLocked();
+ if (!userState.bindLocked()) {
+ callback.onFailure();
+ } else if (userState.isBoundLocked()) {
+ userState.mService.onDetectLanguage(sessionId, request, callback);
+ } else {
+ userState.mPendingRequests.add(new PendingRequest(
+ () -> onDetectLanguage(sessionId, request, callback),
+ callback::onFailure, callback.asBinder(), this, userState));
+ }
+ }
+ }
+
+ @Override
+ public void onSuggestConversationActions(
+ TextClassificationSessionId sessionId,
+ ConversationActions.Request request,
+ IConversationActionsCallback callback) throws RemoteException {
+ Preconditions.checkNotNull(request);
+ Preconditions.checkNotNull(callback);
+
+ synchronized (mLock) {
+ UserState userState = getCallingUserStateLocked();
+ if (!userState.bindLocked()) {
+ callback.onFailure();
+ } else if (userState.isBoundLocked()) {
+ userState.mService.onSuggestConversationActions(sessionId, request, callback);
+ } else {
+ userState.mPendingRequests.add(new PendingRequest(
+ () -> onSuggestConversationActions(sessionId, request, callback),
+ callback::onFailure, callback.asBinder(), this, userState));
+ }
+ }
+ }
+
+ @Override
public void onCreateTextClassificationSession(
TextClassificationContext classificationContext, TextClassificationSessionId sessionId)
throws RemoteException {
@@ -354,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 c4d2851..4b413e5 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -48,8 +48,8 @@
import java.util.concurrent.atomic.AtomicBoolean;
import libcore.icu.ICU;
import libcore.timezone.TzDataSetVersion;
-import libcore.util.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.TimeZoneFinder;
+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/wm/RootWindowContainerListener.java b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
similarity index 60%
rename from services/core/java/com/android/server/wm/RootWindowContainerListener.java
rename to services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
index f413e3f7..7310af3 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainerListener.java
+++ b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-package com.android.server.wm;
+package com.android.server.updates;
-/**
- * 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);
+public class ConversationActionsInstallReceiver extends ConfigUpdateInstallReceiver {
+
+ public ConversationActionsInstallReceiver() {
+ super(
+ "/data/misc/textclassifier/",
+ "actions_suggestions.model",
+ "metadata/actions_suggestions",
+ "version");
+ }
}
diff --git a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
index dfe02ec..05dad21 100644
--- a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
@@ -21,8 +21,8 @@
public LangIdInstallReceiver() {
super(
"/data/misc/textclassifier/",
- "textclassifier.langid.model",
- "metadata/langid",
+ "lang_id.model",
+ "metadata/lang_id",
"version");
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 479f427..cfec8ef 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -18,6 +18,7 @@
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
import static android.os.ParcelFileDescriptor.MODE_CREATE;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
@@ -56,6 +57,7 @@
import android.graphics.BitmapRegionDecoder;
import android.graphics.Color;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
@@ -86,7 +88,6 @@
import android.util.Xml;
import android.view.Display;
import android.view.IWindowManager;
-import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
@@ -118,6 +119,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.function.Consumer;
public class WallpaperManagerService extends IWallpaperManager.Stub
implements IWallpaperManagerService {
@@ -484,6 +486,8 @@
private void generateCrop(WallpaperData wallpaper) {
boolean success = false;
+ // Only generate crop for default display.
+ final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
Rect cropHint = new Rect(wallpaper.cropHint);
if (DEBUG) {
@@ -491,7 +495,7 @@
+ Integer.toHexString(wallpaper.whichPending)
+ " to " + wallpaper.cropFile.getName()
+ " crop=(" + cropHint.width() + 'x' + cropHint.height()
- + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')');
+ + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
}
// Analyse the source; needed in multiple cases
@@ -530,11 +534,11 @@
}
// scale if the crop height winds up not matching the recommended metrics
- needScale = (wallpaper.height != cropHint.height());
+ needScale = (wpData.mHeight != cropHint.height());
if (DEBUG) {
Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
- Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height);
+ Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
}
@@ -564,7 +568,7 @@
// just let the decode take care of it because we also want to remap where the
// cropHint rectangle lies in the decoded [super]rect.
final BitmapFactory.Options scaler;
- final int actualScale = cropHint.height() / wallpaper.height;
+ final int actualScale = cropHint.height() / wpData.mHeight;
int scale = 1;
while (2*scale < actualScale) {
scale *= 2;
@@ -590,17 +594,18 @@
cropHint.offsetTo(0, 0);
cropHint.right /= scale; // adjust by downsampling factor
cropHint.bottom /= scale;
- final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
+ final float heightR =
+ ((float) wpData.mHeight) / ((float) cropHint.height());
if (DEBUG) {
Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
}
final int destWidth = (int)(cropHint.width() * heightR);
final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
- destWidth, wallpaper.height, true);
+ destWidth, wpData.mHeight, true);
if (DEBUG) {
Slog.v(TAG, "Final extract:");
- Slog.v(TAG, " dims: w=" + wallpaper.width
- + " h=" + wallpaper.height);
+ Slog.v(TAG, " dims: w=" + wpData.mWidth
+ + " h=" + wpData.mHeight);
Slog.v(TAG, " out: w=" + finalCrop.getWidth()
+ " h=" + finalCrop.getHeight());
}
@@ -640,6 +645,43 @@
final IPackageManager mIPackageManager;
final MyPackageMonitor mMonitor;
final AppOpsManager mAppOpsManager;
+
+ private final DisplayManager mDisplayManager;
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ synchronized (mLock) {
+ if (mLastWallpaper != null) {
+ final WallpaperConnection.DisplayConnector connector =
+ mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+ if (connector == null) return;
+
+ connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ if (mLastWallpaper != null) {
+ final WallpaperConnection.DisplayConnector connector =
+ mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+ if (connector == null) return;
+ connector.disconnectLocked();
+ mLastWallpaper.connection.removeDisplayConnector(displayId);
+ mLastWallpaper.removeDisplayData(displayId);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ }
+ };
+
/**
* Map of color listeners per user id.
* The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
@@ -738,16 +780,23 @@
private RemoteCallbackList<IWallpaperManagerCallback> callbacks
= new RemoteCallbackList<IWallpaperManagerCallback>();
- int width = -1;
- int height = -1;
+ private static final class DisplayData {
+ int mWidth = -1;
+ int mHeight = -1;
+ final Rect mPadding = new Rect(0, 0, 0, 0);
+ final int mDisplayId;
+
+ DisplayData(int displayId) {
+ mDisplayId = displayId;
+ }
+ }
+ private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
/**
* The crop hint supplied for displaying a subset of the source image
*/
final Rect cropHint = new Rect(0, 0, 0, 0);
- final Rect padding = new Rect(0, 0, 0, 0);
-
WallpaperData(int userId, String inputFileName, String cropFileName) {
this.userId = userId;
final File wallpaperDir = getWallpaperDir(userId);
@@ -763,6 +812,44 @@
boolean sourceExists() {
return wallpaperFile.exists();
}
+
+ void removeDisplayData(int displayId) {
+ mDisplayDatas.remove(displayId);
+ }
+ }
+
+ private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) {
+ WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId);
+ if (wpdData == null) {
+ wpdData = new WallpaperData.DisplayData(displayId);
+ ensureSaneWallpaperDisplaySize(wpdData, displayId);
+ data.mDisplayDatas.append(displayId, wpdData);
+ }
+ return wpdData;
+ }
+
+ private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData,
+ int displayId) {
+ // We always want to have some reasonable width hint.
+ final int baseSize = getMaximumSizeDimension(displayId);
+ if (wpdData.mWidth < baseSize) {
+ wpdData.mWidth = baseSize;
+ }
+ if (wpdData.mHeight < baseSize) {
+ wpdData.mHeight = baseSize;
+ }
+ }
+
+ private int getMaximumSizeDimension(int displayId) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ return display.getMaximumSizeDimension();
+ }
+
+ void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) {
+ for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) {
+ final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i);
+ action.accept(wpdData);
+ }
}
int makeWallpaperIdLocked() {
@@ -775,20 +862,98 @@
class WallpaperConnection extends IWallpaperConnection.Stub
implements ServiceConnection {
+ /**
+ * Collect needed info for a display.
+ */
+ private final class DisplayConnector {
+ final int mDisplayId;
+ final Binder mToken = new Binder();
+ IWallpaperEngine mEngine;
+ boolean mDimensionsChanged;
+ boolean mPaddingChanged;
+
+ DisplayConnector(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ void ensureStatusHandled() {
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper,
+ mDisplayId);
+ if (mDimensionsChanged) {
+ try {
+ mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to set wallpaper dimensions", e);
+ }
+ mDimensionsChanged = false;
+ }
+ if (mPaddingChanged) {
+ try {
+ mEngine.setDisplayPadding(wpdData.mPadding);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to set wallpaper padding", e);
+ }
+ mPaddingChanged = false;
+ }
+ }
+
+ void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
+ if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
+ try {
+ mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed add wallpaper window token on display " + mDisplayId, e);
+ return;
+ }
+
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ mDisplayId);
+ try {
+ connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
+ wpdData.mWidth, wpdData.mHeight,
+ wpdData.mPadding, mDisplayId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed attaching wallpaper on display", e);
+ if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating
+ && connection.getConnectedEngineSize() == 0) {
+ bindWallpaperComponentLocked(null /* componentName */, false /* force */,
+ false /* fromUser */, wallpaper, null /* reply */);
+ }
+ }
+ }
+
+ void disconnectLocked() {
+ if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
+ try {
+ mIWindowManager.removeWindowToken(mToken, mDisplayId);
+ } catch (RemoteException e) {
+ }
+ try {
+ if (mEngine != null) {
+ mEngine.destroy();
+ }
+ } catch (RemoteException e) {
+ }
+ mEngine = null;
+ }
+ }
+
+ /**
+ * A map for each display.
+ * Use {@link #getDisplayConnectorOrCreate(int displayId)} to ensure the display is usable.
+ */
+ private SparseArray<DisplayConnector> mDisplayConnector = new SparseArray<>();
+
/** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the
* middle of an update). If exceeded, the wallpaper gets reset to the system default. */
private static final long WALLPAPER_RECONNECT_TIMEOUT_MS = 10000;
final WallpaperInfo mInfo;
- final Binder mToken = new Binder();
IWallpaperService mService;
- IWallpaperEngine mEngine;
WallpaperData mWallpaper;
+ final int mClientUid;
IRemoteCallback mReply;
- boolean mDimensionsChanged = false;
- boolean mPaddingChanged = false;
-
private Runnable mResetRunnable = () -> {
synchronized (mLock) {
if (mShuttingDown) {
@@ -809,9 +974,64 @@
}
};
- public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
+ WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
mInfo = info;
mWallpaper = wallpaper;
+ mClientUid = clientUid;
+ initDisplayState();
+ }
+
+ private void initDisplayState() {
+ final Display[] displays = mDisplayManager.getDisplays();
+ for (Display display : displays) {
+ if (isUsableDisplay(display)) {
+ final int displayId = display.getDisplayId();
+ mDisplayConnector.append(displayId, new DisplayConnector(displayId));
+ }
+ }
+ }
+
+ // TODO(b/115486823) Support the system decorations change at runtime.
+ private boolean isUsableDisplay(Display display) {
+ return display != null && display.hasAccess(mClientUid)
+ // TODO(b/114338689) Use WindowManager.supportsSystemDecorations when ready
+ && (display.supportsSystemDecorations()
+ || display.getDisplayId() == DEFAULT_DISPLAY);
+ }
+
+ void forEachDisplayConnector(Consumer<DisplayConnector> action) {
+ for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
+ final DisplayConnector connector = mDisplayConnector.valueAt(i);
+ action.accept(connector);
+ }
+ }
+
+ int getConnectedEngineSize() {
+ int engineSize = 0;
+ for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
+ final DisplayConnector connector = mDisplayConnector.valueAt(i);
+ if (connector.mEngine != null) engineSize++;
+ }
+ return engineSize;
+ }
+
+ DisplayConnector getDisplayConnectorOrCreate(int displayId) {
+ DisplayConnector connector = mDisplayConnector.get(displayId);
+ if (connector == null) {
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (isUsableDisplay(display)) {
+ connector = new DisplayConnector(displayId);
+ mDisplayConnector.append(displayId, connector);
+ }
+ }
+ return connector;
+ }
+
+ void removeDisplayConnector(int displayId) {
+ final DisplayConnector connector = mDisplayConnector.get(displayId);
+ if (connector != null) {
+ mDisplayConnector.remove(displayId);
+ }
}
@Override
@@ -839,7 +1059,7 @@
+ mWallpaper.wallpaperComponent);
}
mService = null;
- mEngine = null;
+ forEachDisplayConnector(connector -> connector.mEngine = null);
if (mWallpaper.connection == this) {
// There is an inherent ordering race between this callback and the
// package monitor that receives notice that a package is being updated,
@@ -863,7 +1083,8 @@
fgHandler.removeCallbacks(mResetRunnable);
fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
if (DEBUG_LIVE) {
- Slog.i(TAG, "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
+ Slog.i(TAG,
+ "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
}
}
@@ -943,38 +1164,38 @@
}
@Override
- public void attachEngine(IWallpaperEngine engine) {
+ public void attachEngine(IWallpaperEngine engine, int displayId) {
synchronized (mLock) {
- mEngine = engine;
- if (mDimensionsChanged) {
+ final DisplayConnector connector = getDisplayConnectorOrCreate(displayId);
+ if (connector == null) {
try {
- mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+ engine.destroy();
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to set wallpaper dimensions", e);
+ Slog.w(TAG, "Failed to destroy engine", e);
}
- mDimensionsChanged = false;
+ return;
}
- if (mPaddingChanged) {
+ connector.mEngine = engine;
+ connector.ensureStatusHandled();
+
+ // TODO(multi-display) TBD.
+ if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) {
try {
- mEngine.setDisplayPadding(mWallpaper.padding);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to set wallpaper padding", e);
- }
- mPaddingChanged = false;
- }
- if (mInfo != null && mInfo.supportsAmbientMode()) {
- try {
- mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
+ connector.mEngine.setInAmbientMode(mInAmbientMode, 0L /* duration */);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to set ambient mode state", e);
}
}
- try {
- // This will trigger onComputeColors in the wallpaper engine.
- // It's fine to be locked in here since the binder is oneway.
- mEngine.requestWallpaperColors();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to request wallpaper colors", e);
+ // TODO(multi-display) So far, we have shared the same wallpaper on each display.
+ // Once we have multiple wallpapers on multiple displays, please complete here.
+ if (displayId == DEFAULT_DISPLAY) {
+ try {
+ // This will trigger onComputeColors in the wallpaper engine.
+ // It's fine to be locked in here since the binder is oneway.
+ connector.mEngine.requestWallpaperColors();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to request wallpaper colors", e);
+ }
}
}
}
@@ -1162,6 +1383,8 @@
ServiceManager.getService(Context.WINDOW_SERVICE));
mIPackageManager = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
mMonitor = new MyPackageMonitor();
mColorsChangedListeners = new SparseArray<>();
}
@@ -1396,7 +1619,7 @@
wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
final WallpaperData fallback = new WallpaperData(wallpaper.userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
- ensureSaneWallpaperData(fallback);
+ ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
mWaitingForUnlock = true;
}
@@ -1541,7 +1764,15 @@
return false;
}
- public void setDimensionHints(int width, int height, String callingPackage)
+ private boolean isValidDisplay(int displayId) {
+ return mDisplayManager.getDisplay(displayId) != null;
+ }
+
+ /**
+ * Sets the dimension hint for the wallpaper. These hints indicate the desired
+ * minimum width and height for the wallpaper in a particular display.
+ */
+ public void setDimensionHints(int width, int height, String callingPackage, int displayId)
throws RemoteException {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
if (!isWallpaperSupported(callingPackage)) {
@@ -1554,80 +1785,113 @@
throw new IllegalArgumentException("width and height must be > 0");
}
- if (width != wallpaper.width || height != wallpaper.height) {
- wallpaper.width = width;
- wallpaper.height = height;
- saveSettingsLocked(userId);
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
+
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+ if (width != wpdData.mWidth || height != wpdData.mHeight) {
+ wpdData.mWidth = width;
+ wpdData.mHeight = height;
+ if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
- if (wallpaper.connection.mEngine != null) {
+ final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+ .getDisplayConnectorOrCreate(displayId);
+ final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
+ if (engine != null) {
try {
- wallpaper.connection.mEngine.setDesiredSize(
- width, height);
+ engine.setDesiredSize(width, height);
} catch (RemoteException e) {
}
notifyCallbacksLocked(wallpaper);
- } else if (wallpaper.connection.mService != null) {
+ } else if (wallpaper.connection.mService != null && connector != null) {
// We've attached to the service but the engine hasn't attached back to us
// yet. This means it will be created with the previous dimensions, so we
// need to update it to the new dimensions once it attaches.
- wallpaper.connection.mDimensionsChanged = true;
+ connector.mDimensionsChanged = true;
}
}
}
}
}
- public int getWidthHint() throws RemoteException {
+ /**
+ * Returns the desired minimum width for the wallpaper in a particular display.
+ */
+ public int getWidthHint(int displayId) throws RemoteException {
synchronized (mLock) {
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
if (wallpaper != null) {
- return wallpaper.width;
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ displayId);
+ return wpdData.mWidth;
} else {
return 0;
}
}
}
- public int getHeightHint() throws RemoteException {
+ /**
+ * Returns the desired minimum height for the wallpaper in a particular display.
+ */
+ public int getHeightHint(int displayId) throws RemoteException {
synchronized (mLock) {
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
if (wallpaper != null) {
- return wallpaper.height;
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ displayId);
+ return wpdData.mHeight;
} else {
return 0;
}
}
}
- public void setDisplayPadding(Rect padding, String callingPackage) {
+ /**
+ * Sets extra padding that we would like the wallpaper to have outside of the display.
+ */
+ public void setDisplayPadding(Rect padding, String callingPackage, int displayId) {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
if (!isWallpaperSupported(callingPackage)) {
return;
}
synchronized (mLock) {
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
throw new IllegalArgumentException("padding must be positive: " + padding);
}
- if (!padding.equals(wallpaper.padding)) {
- wallpaper.padding.set(padding);
- saveSettingsLocked(userId);
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+ if (!padding.equals(wpdData.mPadding)) {
+ wpdData.mPadding.set(padding);
+ if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
- if (wallpaper.connection.mEngine != null) {
+ final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+ .getDisplayConnectorOrCreate(displayId);
+ final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
+ if (engine != null) {
try {
- wallpaper.connection.mEngine.setDisplayPadding(padding);
+ engine.setDisplayPadding(padding);
} catch (RemoteException e) {
}
notifyCallbacksLocked(wallpaper);
- } else if (wallpaper.connection.mService != null) {
+ } else if (wallpaper.connection.mService != null && connector != null) {
// We've attached to the service but the engine hasn't attached back to us
// yet. This means it will be created with the previous dimensions, so we
// need to update it to the new dimensions once it attaches.
- wallpaper.connection.mPaddingChanged = true;
+ connector.mPaddingChanged = true;
}
}
}
@@ -1675,10 +1939,13 @@
// user switch)
return null;
}
+ // Only for default display.
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ DEFAULT_DISPLAY);
try {
if (outParams != null) {
- outParams.putInt("width", wallpaper.width);
- outParams.putInt("height", wallpaper.height);
+ outParams.putInt("width", wpdData.mWidth);
+ outParams.putInt("height", wpdData.mHeight);
}
if (cb != null) {
wallpaper.callbacks.register(cb);
@@ -1756,14 +2023,22 @@
}
}
- 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()) {
- engine = data.connection.mEngine;
+ // TODO(b/115486823) Extends this method with specific display.
+ engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
} else {
engine = null;
}
@@ -1771,7 +2046,7 @@
if (engine != null) {
try {
- engine.setInAmbientMode(inAmbienMode, animated);
+ engine.setInAmbientMode(inAmbientMode, animationDuration);
} catch (RemoteException e) {
// Cannot talk to wallpaper engine.
}
@@ -1898,10 +2173,14 @@
// We know a-priori that there is no lock-only wallpaper currently
WallpaperData lockWP = new WallpaperData(userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
+ final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP,
+ DEFAULT_DISPLAY);
+ final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP,
+ DEFAULT_DISPLAY);
lockWP.wallpaperId = sysWP.wallpaperId;
lockWP.cropHint.set(sysWP.cropHint);
- lockWP.width = sysWP.width;
- lockWP.height = sysWP.height;
+ lockWPDData.mWidth = sysWPDData.mWidth;
+ lockWPDData.mHeight = sysWPDData.mHeight;
lockWP.allowBackup = sysWP.allowBackup;
lockWP.primaryColors = sysWP.primaryColors;
@@ -2071,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) {
@@ -2123,9 +2402,27 @@
}
}
+ 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);
- WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
+ final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
+ MATCH_DIRECT_BOOT_AUTO, wallpaper.userId);
+ WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid);
intent.setComponent(componentName);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.wallpaper_binding_label);
@@ -2152,14 +2449,8 @@
wallpaper.wallpaperComponent = componentName;
wallpaper.connection = newConn;
newConn.mReply = reply;
- try {
- if (wallpaper.userId == mCurrentUserId) {
- if (DEBUG)
- Slog.v(TAG, "Adding window token: " + newConn.mToken);
- mIWindowManager.addWindowToken(newConn.mToken, TYPE_WALLPAPER, DEFAULT_DISPLAY);
- mLastWallpaper = wallpaper;
- }
- } catch (RemoteException e) {
+ if (wallpaper.userId == mCurrentUserId) {
+ mLastWallpaper = wallpaper;
}
} catch (RemoteException e) {
String msg = "Remote exception for " + componentName + "\n" + e;
@@ -2181,22 +2472,12 @@
}
wallpaper.connection.mReply = null;
}
- if (wallpaper.connection.mEngine != null) {
- try {
- wallpaper.connection.mEngine.destroy();
- } catch (RemoteException e) {
- }
- }
mContext.unbindService(wallpaper.connection);
- try {
- if (DEBUG)
- Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
- mIWindowManager.removeWindowToken(wallpaper.connection.mToken, DEFAULT_DISPLAY);
- } catch (RemoteException e) {
- }
+ wallpaper.connection.forEachDisplayConnector(connector -> connector.disconnectLocked());
wallpaper.connection.mService = null;
- wallpaper.connection.mEngine = null;
+ wallpaper.connection.mDisplayConnector.clear();
wallpaper.connection = null;
+ if (wallpaper == mLastWallpaper) mLastWallpaper = null;
}
}
@@ -2206,16 +2487,7 @@
}
void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
- try {
- conn.mService.attach(conn, conn.mToken,
- TYPE_WALLPAPER, false,
- wallpaper.width, wallpaper.height, wallpaper.padding);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
- if (!wallpaper.wallpaperUpdating) {
- bindWallpaperComponentLocked(null, false, false, wallpaper, null);
- }
- }
+ conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
}
private void notifyCallbacksLocked(WallpaperData wallpaper) {
@@ -2324,27 +2596,29 @@
if (DEBUG) {
Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
}
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ DEFAULT_DISPLAY);
out.startTag(null, tag);
out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
- out.attribute(null, "width", Integer.toString(wallpaper.width));
- out.attribute(null, "height", Integer.toString(wallpaper.height));
+ out.attribute(null, "width", Integer.toString(wpdData.mWidth));
+ out.attribute(null, "height", Integer.toString(wpdData.mHeight));
out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
- if (wallpaper.padding.left != 0) {
- out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
+ if (wpdData.mPadding.left != 0) {
+ out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left));
}
- if (wallpaper.padding.top != 0) {
- out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
+ if (wpdData.mPadding.top != 0) {
+ out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top));
}
- if (wallpaper.padding.right != 0) {
- out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
+ if (wpdData.mPadding.right != 0) {
+ out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right));
}
- if (wallpaper.padding.bottom != 0) {
- out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
+ if (wpdData.mPadding.bottom != 0) {
+ out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom));
}
if (wallpaper.primaryColors != null) {
@@ -2447,14 +2721,14 @@
wallpaper = new WallpaperData(userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
mLockWallpaperMap.put(userId, wallpaper);
- ensureSaneWallpaperData(wallpaper);
+ ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
} else {
// sanity fallback: we're in bad shape, but establishing a known
// valid system+lock WallpaperData will keep us from dying.
Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
mWallpaperMap.put(userId, wallpaper);
- ensureSaneWallpaperData(wallpaper);
+ ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
}
}
}
@@ -2483,6 +2757,8 @@
}
}
boolean success = false;
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ DEFAULT_DISPLAY);
try {
stream = new FileInputStream(file);
XmlPullParser parser = Xml.newPullParser();
@@ -2509,8 +2785,8 @@
}
if (DEBUG) {
- Slog.v(TAG, "mWidth:" + wallpaper.width);
- Slog.v(TAG, "mHeight:" + wallpaper.height);
+ Slog.v(TAG, "mWidth:" + wpdData.mWidth);
+ Slog.v(TAG, "mHeight:" + wpdData.mHeight);
Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
Slog.v(TAG, "mName:" + wallpaper.name);
@@ -2546,10 +2822,10 @@
IoUtils.closeQuietly(stream);
if (!success) {
- wallpaper.width = -1;
- wallpaper.height = -1;
+ wpdData.mWidth = -1;
+ wpdData.mHeight = -1;
wallpaper.cropHint.set(0, 0, 0, 0);
- wallpaper.padding.set(0, 0, 0, 0);
+ wpdData.mPadding.set(0, 0, 0, 0);
wallpaper.name = "";
mLockWallpaperMap.remove(userId);
@@ -2563,26 +2839,22 @@
}
}
- ensureSaneWallpaperData(wallpaper);
+ ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
if (lockWallpaper != null) {
- ensureSaneWallpaperData(lockWallpaper);
+ ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY);
}
}
- private void ensureSaneWallpaperData(WallpaperData wallpaper) {
- // We always want to have some reasonable width hint.
- int baseSize = getMaximumSizeDimension();
- if (wallpaper.width < baseSize) {
- wallpaper.width = baseSize;
- }
- if (wallpaper.height < baseSize) {
- wallpaper.height = baseSize;
- }
- // and crop, if not previously specified
- if (wallpaper.cropHint.width() <= 0
- || wallpaper.cropHint.height() <= 0) {
- wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
+ private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
+ final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId);
+
+ if (displayId == DEFAULT_DISPLAY) {
+ // crop, if not previously specified
+ if (wallpaper.cropHint.width() <= 0
+ || wallpaper.cropHint.height() <= 0) {
+ wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight);
+ }
}
}
@@ -2598,19 +2870,20 @@
wallpaper.wallpaperId = makeWallpaperIdLocked();
}
+ final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+
if (!keepDimensionHints) {
- wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
- wallpaper.height = Integer.parseInt(parser
- .getAttributeValue(null, "height"));
+ wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
+ wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
}
wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
- wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
- wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
- wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
- wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
+ wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0);
+ wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
+ wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
+ wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
int colorsCount = getAttributeInt(parser, "colorsCount", 0);
if (colorsCount > 0) {
Color primary = null, secondary = null, tertiary = null;
@@ -2633,12 +2906,6 @@
wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
}
- private int getMaximumSizeDimension() {
- WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- Display d = wm.getDefaultDisplay();
- return d.getMaximumSizeDimension();
- }
-
// Called by SystemBackupAgent after files are restored to disk.
public void settingsRestored() {
// Verify caller is the system
@@ -2678,7 +2945,7 @@
if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
+ " id=" + wallpaper.wallpaperId);
if (success) {
- generateCrop(wallpaper); // based on the new image + metadata
+ generateCrop(wallpaper); // based on the new image + metadata
bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
wallpaper, null);
}
@@ -2783,12 +3050,16 @@
WallpaperData wallpaper = mWallpaperMap.valueAt(i);
pw.print(" User "); pw.print(wallpaper.userId);
pw.print(": id="); pw.println(wallpaper.wallpaperId);
- pw.print(" mWidth=");
- pw.print(wallpaper.width);
- pw.print(" mHeight=");
- pw.println(wallpaper.height);
+ forEachDisplayData(wallpaper, wpSize -> {
+ pw.print(" displayId=");
+ pw.println(wpSize.mDisplayId);
+ pw.print(" mWidth=");
+ pw.print(wpSize.mWidth);
+ pw.print(" mHeight=");
+ pw.println(wpSize.mHeight);
+ pw.print(" mPadding="); pw.println(wpSize.mPadding);
+ });
pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
- pw.print(" mPadding="); pw.println(wallpaper.padding);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
@@ -2801,12 +3072,16 @@
pw.print(" mInfo.component=");
pw.println(conn.mInfo.getComponent());
}
- pw.print(" mToken=");
- pw.println(conn.mToken);
+ conn.forEachDisplayConnector(connector -> {
+ pw.print(" mDisplayId=");
+ pw.println(connector.mDisplayId);
+ pw.print(" mToken=");
+ pw.println(connector.mToken);
+ pw.print(" mEngine=");
+ pw.println(connector.mEngine);
+ });
pw.print(" mService=");
pw.println(conn.mService);
- pw.print(" mEngine=");
- pw.println(conn.mEngine);
pw.print(" mLastDiedTime=");
pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
}
@@ -2815,11 +3090,15 @@
for (int i = 0; i < mLockWallpaperMap.size(); i++) {
WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
pw.print(" User "); pw.print(wallpaper.userId);
- pw.print(": id="); pw.println(wallpaper.wallpaperId);
- pw.print(" mWidth="); pw.print(wallpaper.width);
- pw.print(" mHeight="); pw.println(wallpaper.height);
+ pw.print(": id="); pw.println(wallpaper.wallpaperId);
+ forEachDisplayData(wallpaper, wpSize -> {
+ pw.print(" displayId=");
+ pw.println(wpSize.mDisplayId);
+ pw.print(" mWidth="); pw.print(wpSize.mWidth);
+ pw.print(" mHeight="); pw.println(wpSize.mHeight);
+ pw.print(" mPadding="); pw.println(wpSize.mPadding);
+ });
pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
- pw.print(" mPadding="); pw.println(wallpaper.padding);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
}
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..84750b3 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
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 1c08d03..416e133 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -277,7 +277,8 @@
mLastLogTimeSecs = now;
mWindowState = WINDOW_STATE_INVALID;
- ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+ ActivityStack stack =
+ mSupervisor.mRootActivityContainer.getTopDisplayFocusedStack();
if (stack == null) {
return;
}
@@ -289,7 +290,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) {
@@ -850,9 +851,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 +883,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.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));
+ }
}
mMetricsLogger.write(builder);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c43e64e..b4aec35 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;
@@ -225,7 +246,9 @@
final ActivityTaskManagerService service; // 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
@@ -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;
@@ -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 {
@@ -844,6 +874,7 @@
boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
ActivityOptions options, ActivityRecord sourceRecord) {
service = _service;
+ mRootActivityContainer = _service.mRootActivityContainer;
appToken = new Token(this, _intent);
info = aInfo;
launchedFromPid = _launchedFromPid;
@@ -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 = service.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(service.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, realActivity, 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 (service.mWindowManager.mRoot == null) return;
+
+ final DisplayContent dc = service.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.
@@ -1200,7 +1283,7 @@
}
boolean isFocusable() {
- return mStackSupervisor.isFocusable(this, isAlwaysFocusable());
+ return mRootActivityContainer.isFocusable(this, isAlwaysFocusable());
}
boolean isResizeable() {
@@ -1353,7 +1436,7 @@
return false;
}
- if (mStackSupervisor.getTopResumedActivity() == this) {
+ if (mRootActivityContainer.getTopResumedActivity() == this) {
if (DEBUG_FOCUS) {
Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
}
@@ -1366,7 +1449,7 @@
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);
}
@@ -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;
@@ -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);
+ }
}
}
@@ -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);
}
}
}
@@ -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,7 +2163,10 @@
info.windowsFullyDrawnDelayMs);
}
}
- @Override
+
+ /**
+ * Called when the starting window for this container is drawn.
+ */
public void onStartingWindowDrawn(long timestamp) {
synchronized (service.mGlobalLock) {
mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
@@ -1945,7 +2174,7 @@
}
}
- @Override
+ /** Called when the windows associated app window container are drawn. */
public void onWindowsDrawn(boolean drawn, long timestamp) {
synchronized (service.mGlobalLock) {
mDrawn = drawn;
@@ -1965,7 +2194,7 @@
}
}
- @Override
+ /** Called when the windows associated app window container are visible. */
public void onWindowsVisible() {
synchronized (service.mGlobalLock) {
mStackSupervisor.reportActivityVisibleLocked(this);
@@ -1999,7 +2228,7 @@
}
}
- @Override
+ /** Called when the windows associated app window container are no longer visible. */
public void onWindowsGone() {
synchronized (service.mGlobalLock) {
if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
@@ -2007,7 +2236,14 @@
}
}
- @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;
@@ -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) {
@@ -2183,7 +2419,7 @@
void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
boolean fromRecents) {
- if (mWindowContainerController == null) {
+ if (mAppWindowToken == null) {
return;
}
if (mTaskOverlay) {
@@ -2198,7 +2434,7 @@
final CompatibilityInfo compatInfo =
service.compatibilityInfoForPackageLocked(info.applicationInfo);
- final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
+ 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,
false /* deferResume */, displayId)) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
service.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 service.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);
}
/**
@@ -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;
@@ -2354,7 +2618,7 @@
// bounds would end up too small.
outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top);
- if (service.mWindowManager.getNavBarPosition() == NAV_BAR_LEFT) {
+ if (service.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;
@@ -2867,7 +3131,7 @@
void setShowWhenLocked(boolean showWhenLocked) {
mShowWhenLocked = showWhenLocked;
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */,
+ mRootActivityContainer.ensureActivitiesVisible(null, 0 /* configChanges */,
false /* preserveWindows */);
}
@@ -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
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 8d467c8..9fbeaf8 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,7 +534,7 @@
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);
}
@@ -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.
@@ -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() {
@@ -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);
@@ -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;
}
@@ -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;
}
@@ -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,15 +2545,20 @@
// 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;
- if (!mService.mShuttingDown && !mTopActivityOccludesKeyguard
- && next.canShowWhenLocked()) {
- ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS);
- nothingToResume = shouldSleepActivities();
+ if (!mService.mShuttingDown) {
+ final boolean canShowWhenLocked = !mTopActivityOccludesKeyguard
+ && next.canShowWhenLocked();
+ final boolean mayDismissKeyguard = mTopDismissingKeyguardActivity != next
+ && next.hasDismissKeyguardWindows();
+ if (canShowWhenLocked || mayDismissKeyguard) {
+ ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS);
+ nothingToResume = shouldSleepActivities();
+ }
}
if (nothingToResume) {
// Make sure we have executed any pending transitions, since there
@@ -2560,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;
}
}
@@ -2571,7 +2576,6 @@
if (!mService.mAmInternal.hasStartedUserState(next.userId)) {
Slog.w(TAG, "Skipping resume of top activity " + next
+ ": user " + next.userId + " is stopped");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -2585,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;
}
@@ -2635,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);
}
@@ -2650,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;
}
@@ -2668,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);
@@ -2809,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 */);
}
@@ -2831,7 +2832,6 @@
next.setVisibility(true);
}
next.completeResumeLocked();
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2894,7 +2894,6 @@
false /* taskSwitch */);
}
mStackSupervisor.startSpecificActivityLocked(next, true, false);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2908,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 {
@@ -2926,7 +2924,6 @@
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2937,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 */);
}
@@ -2945,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. */
@@ -3038,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;
}
@@ -3068,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) {
@@ -3531,7 +3528,7 @@
}
private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
- if (!mStackSupervisor.isTopDisplayFocusedStack(this) ||
+ if (!mRootActivityContainer.isTopDisplayFocusedStack(this) ||
((mResumedActivity != r) && (mResumedActivity != null))) {
return;
}
@@ -3540,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);
}
@@ -3564,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().
@@ -3592,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;
@@ -4013,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 +
@@ -4030,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;
}
@@ -4372,7 +4369,7 @@
}
}
if (activityRemoved) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
@@ -4563,7 +4560,7 @@
}
}
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
@@ -4707,7 +4704,7 @@
task.mLastTimeMoved *= -1;
}
}
- mStackSupervisor.invalidateTaskLayers();
+ mRootActivityContainer.invalidateTaskLayers();
}
final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
@@ -4783,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);
@@ -4855,7 +4852,7 @@
return true;
}
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
return true;
}
@@ -4902,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();
}
}
@@ -5094,7 +5091,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);
@@ -5159,7 +5156,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) {
@@ -5306,7 +5303,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);
@@ -5412,7 +5409,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) {
@@ -5479,7 +5476,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 694e9d1..3162ee3 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,20 +82,11 @@
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;
@@ -139,17 +109,11 @@
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 +127,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 +142,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 +184,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 +215,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<>();
@@ -315,20 +241,20 @@
/** The number of distinct task ids that can be assigned to the tasks of a single user */
private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
- ActivityTaskManagerService mService;
+ 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 +267,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 +315,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 +330,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 +340,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 +363,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 +382,6 @@
*/
private boolean mAllowDockedStackResize = true;
- /**
- * Is dock currently minimized.
- */
- boolean mIsDockMinimized;
-
private KeyguardController mKeyguardController;
private PowerManager mPowerManager;
@@ -577,8 +389,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,16 +427,6 @@
mHandler = new ActivityStackSupervisorHandler(looper);
}
- @VisibleForTesting
- void setService(ActivityTaskManagerService service) {
- mService = service;
- }
-
- @VisibleForTesting
- void setWindowContainerController(RootWindowContainerController controller) {
- mWindowContainerController = controller;
- }
-
public void initialize() {
if (mInitialized) {
return;
@@ -681,321 +481,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) {
@@ -1019,7 +514,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) {
@@ -1034,145 +529,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);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (!isTopDisplayFocusedStack(stack)) {
- continue;
- }
- 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);
@@ -1263,24 +619,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;
@@ -1360,10 +698,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.
@@ -1398,7 +736,7 @@
// 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 */);
}
@@ -1568,7 +906,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();
}
@@ -1581,47 +919,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) {
@@ -1677,47 +974,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,
@@ -1796,7 +1052,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;
@@ -1858,21 +1115,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 {
@@ -2024,7 +1266,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();
}
@@ -2033,7 +1276,7 @@
r.mRelaunchReason = RELAUNCH_REASON_NONE;
}
- if (allResumedActivitiesIdle()) {
+ if (mRootActivityContainer.allResumedActivitiesIdle()) {
if (r != null) {
mService.scheduleAppGcsLocked();
}
@@ -2046,7 +1289,7 @@
}
mLaunchingActivity.release();
}
- ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
// Atomically retrieve all of the other things to do.
@@ -2102,186 +1345,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();
@@ -2301,7 +1371,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);
@@ -2313,7 +1384,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 {
@@ -2366,387 +1437,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();
- }
-
- // 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) {
- 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();
- }
- return activityDisplay.createStack(
- windowingMode,
- options != null ? options.getLaunchActivityType() : r.getActivityType(),
- 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);
}
@@ -2765,7 +1471,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.
@@ -2822,8 +1529,8 @@
}
}
- ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
- resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
} finally {
mAllowDockedStackResize = true;
mWindowManager.continueSurfaceLayout();
@@ -2869,7 +1576,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) {
@@ -2878,7 +1585,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;
@@ -2917,7 +1625,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);
@@ -2937,7 +1645,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);
@@ -2955,7 +1664,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;
@@ -3036,22 +1746,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,
@@ -3072,7 +1766,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);
@@ -3161,7 +1856,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
@@ -3181,7 +1877,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;
}
@@ -3203,39 +1899,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.
*/
@@ -3287,159 +1950,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()) {
@@ -3453,24 +1963,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 {
@@ -3500,51 +2005,6 @@
}
}
- 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();
@@ -3561,12 +2021,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();
@@ -3578,52 +2039,24 @@
}
}
- // 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)) {
+ 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();
@@ -3646,157 +2079,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);
}
@@ -3821,7 +2106,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);
@@ -3871,134 +2156,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) {
@@ -4014,73 +2191,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) {
@@ -4190,294 +2300,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,
@@ -4623,21 +2445,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);
}
@@ -4656,10 +2463,8 @@
mDeferResumeCount--;
}
- /**
- * @return True if resume can be called.
- */
- private boolean readyToResume() {
+ /** @return True if resume can be called. */
+ boolean readyToResume() {
return mDeferResumeCount == 0;
}
@@ -4711,7 +2516,7 @@
} break;
case RESUME_TOP_ACTIVITY_MSG: {
synchronized (mService.mGlobalLock) {
- resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
} break;
case SLEEP_TIMEOUT_MSG: {
@@ -4747,19 +2552,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.
*
@@ -4804,8 +2596,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();
@@ -4818,7 +2610,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
@@ -4827,7 +2620,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,
@@ -4880,35 +2674,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 {
@@ -4946,30 +2711,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..54a63a1 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,7 +281,7 @@
mActivityOptions = ActivityOptions.makeBasic();
}
- ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity();
+ ActivityRecord homeActivityRecord = mRootActivityContainer.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.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d4c1bca..cba1044 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) {
@@ -731,6 +735,12 @@
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, callerApp);
+ }
+
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
@@ -774,6 +784,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 +823,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 +860,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 +879,54 @@
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,
+ 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's process is important enough
+ if (mService.getUidStateLocked(callingUid) <= ActivityManager.PROCESS_STATE_TOP) {
+ return false;
+ }
+ // don't abort if the callingUid has any visible window
+ if (mService.mWindowManager.isAnyWindowVisibleForUid(callingUid)) {
+ 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;
+ }
+
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 +942,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 +1111,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,
@@ -1249,7 +1297,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 */);
}
}
@@ -1284,7 +1333,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;
@@ -1361,7 +1410,8 @@
}
}
- mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);
+ mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded
+ (false /* forceSend */, reusedActivity);
reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
@@ -1413,7 +1463,7 @@
// 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
@@ -1430,7 +1480,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) {
@@ -1485,7 +1535,8 @@
EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
mTargetStack.mLastPausedActivity = null;
- mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
+ mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+ false /* forceSend */, mStartActivity);
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
mOptions);
@@ -1512,16 +1563,16 @@
// 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.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+ mRootActivityContainer.updateUserStack(mStartActivity.userId, mTargetStack);
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
mPreferredDisplayId, mTargetStack);
@@ -1642,7 +1693,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,7 +1729,7 @@
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)) {
@@ -1840,26 +1891,28 @@
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);
}
}
- if (mStartActivity.isActivityTypeHome() && intentActivity != null
+ if (intentActivity != null
+ && (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome())
&& intentActivity.getDisplayId() != mPreferredDisplayId) {
// Do not reuse home activity on other displays.
intentActivity = null;
@@ -2066,11 +2119,11 @@
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.userId, mTargetStack);
}
private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
@@ -2144,13 +2197,13 @@
// 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 */);
}
}
@@ -2181,7 +2234,7 @@
// 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;
@@ -2199,7 +2252,7 @@
deliverNewIntent(top);
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
return START_DELIVERED_TO_TOP;
}
@@ -2253,7 +2306,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");
@@ -2347,7 +2401,7 @@
}
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,
@@ -2368,18 +2422,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);
@@ -2389,7 +2443,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();
@@ -2435,14 +2489,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;
@@ -2460,7 +2514,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 {
@@ -2468,10 +2523,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;
}
@@ -2659,7 +2714,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/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 4f01d699..11e998c 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;
@@ -341,7 +341,8 @@
ActivityManagerInternal mAmInternal;
UriGrantsManagerInternal mUgmInternal;
private PackageManagerInternal mPmInternal;
- private ActivityTaskManagerInternal mInternal;
+ @VisibleForTesting
+ final ActivityTaskManagerInternal mInternal;
PowerManagerInternal mPowerManagerInternal;
private UsageStatsManagerInternal mUsageStatsInternal;
@@ -351,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;
@@ -643,6 +645,7 @@
mSystemThread = ActivityThread.currentActivityThread();
mUiContext = mSystemThread.getSystemUiContext();
mLifecycleManager = new ClientLifecycleManager();
+ mInternal = new LocalService();
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
}
@@ -764,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);
@@ -799,6 +803,7 @@
mWindowManager = wm;
mLockTaskController.setWindowManager(wm);
mStackSupervisor.setWindowManager(wm);
+ mRootActivityContainer.setWindowManager(wm);
}
}
@@ -893,7 +898,6 @@
}
private void start() {
- mInternal = new LocalService();
LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
}
@@ -1254,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);
}
@@ -1798,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;
@@ -1828,7 +1832,7 @@
if (translucentChanged) {
r.getStack().convertActivityToTranslucent(r);
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
mWindowManager.setAppFullscreen(token, false);
return translucentChanged;
}
@@ -1841,7 +1845,7 @@
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);
}
@@ -1878,7 +1882,7 @@
synchronized (mGlobalLock) {
ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack != null) {
- return mStackSupervisor.getStackInfo(focusedStack.mStackId);
+ return mRootActivityContainer.getStackInfo(focusedStack.mStackId);
}
return null;
}
@@ -1894,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 {
@@ -1916,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 {
@@ -2008,7 +2012,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);
}
@@ -2026,7 +2030,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");
@@ -2057,7 +2061,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;
@@ -2077,7 +2081,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);
@@ -2166,7 +2170,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);
@@ -2283,7 +2287,7 @@
final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
callingUid);
- mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
+ mRootActivityContainer.getRunningTasks(maxNum, list, ignoreActivityType,
ignoreWindowingMode, callingUid, allowed);
}
@@ -2319,7 +2323,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;
@@ -2328,7 +2332,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);
@@ -2358,7 +2362,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;
@@ -2370,12 +2374,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);
}
@@ -2409,7 +2413,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);
@@ -2451,7 +2455,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- mStackSupervisor.removeStacksInWindowingModes(windowingModes);
+ mRootActivityContainer.removeStacksInWindowingModes(windowingModes);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2466,7 +2470,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
+ mRootActivityContainer.removeStacksWithActivityTypes(activityTypes);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2497,7 +2501,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- return mStackSupervisor.getAllStackInfosLocked();
+ return mRootActivityContainer.getAllStackInfos();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2510,7 +2514,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- return mStackSupervisor.getStackInfo(windowingMode, activityType);
+ return mRootActivityContainer.getStackInfo(windowingMode, activityType);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2552,7 +2556,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;
@@ -2594,7 +2598,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");
}
@@ -2609,7 +2613,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 {
@@ -2711,7 +2715,7 @@
try {
// TODO: VI Consider treating local voice interactions and voice tasks
// differently here
- mStackSupervisor.finishVoiceTask(session);
+ mRootActivityContainer.finishVoiceTask(session);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -2901,7 +2905,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");
@@ -2917,7 +2921,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");
@@ -2982,7 +2986,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);
}
@@ -3076,7 +3080,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;
@@ -3101,7 +3105,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);
}
@@ -3563,13 +3567,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="
@@ -3624,7 +3628,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;
@@ -3634,7 +3638,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.
@@ -3665,7 +3669,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;
@@ -3707,7 +3711,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(
@@ -3742,7 +3746,7 @@
long ident = Binder.clearCallingIdentity();
try {
- return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
+ return mRootActivityContainer.moveTopStackActivityToPinnedStack(stackId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3820,7 +3824,7 @@
// 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();
stack.setPictureInPictureAspectRatio(aspectRatio);
@@ -4099,7 +4103,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="
@@ -4189,7 +4193,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");
@@ -4209,7 +4213,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");
@@ -4429,7 +4433,7 @@
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 */);
}
@@ -4443,7 +4447,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);
}
}
});
@@ -4495,7 +4499,7 @@
}
ActivityStack getTopDisplayFocusedStack() {
- return mStackSupervisor.getTopDisplayFocusedStack();
+ return mRootActivityContainer.getTopDisplayFocusedStack();
}
/** Pokes the task persister. */
@@ -4507,6 +4511,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) {
@@ -4556,12 +4575,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;
@@ -4583,7 +4602,7 @@
void dumpActivityContainersLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
- mStackSupervisor.dumpChildrenNames(pw, " ");
+ mRootActivityContainer.dumpChildrenNames(pw, " ");
pw.println(" ");
}
@@ -4607,7 +4626,7 @@
ArrayList<ActivityRecord> activities;
synchronized (mGlobalLock) {
- activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
+ activities = mRootActivityContainer.getDumpActivities(name, dumpVisibleStacksOnly,
dumpFocusedStackOnly);
}
@@ -4682,7 +4701,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());
}
@@ -4727,7 +4746,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,
@@ -4859,7 +4878,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.
@@ -4906,7 +4925,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;
@@ -4960,12 +4979,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) {
@@ -5015,6 +5034,10 @@
return mAmInternal.isActivityStartsLoggingEnabled();
}
+ boolean isBackgroundActivityStartsEnabled() {
+ return mAmInternal.isBackgroundActivityStartsEnabled();
+ }
+
void enableScreenAfterBoot(boolean booted) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
SystemClock.uptimeMillis());
@@ -5095,7 +5118,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) {
@@ -5156,14 +5179,15 @@
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;
@@ -5179,7 +5203,7 @@
mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
mStackSupervisor.comeOutOfSleepIfNeededLocked();
}
- mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
+ mRootActivityContainer.applySleepTokens(true /* applyToStacks */);
if (wasSleeping) {
updateOomAdj = true;
}
@@ -5355,7 +5379,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);
}
@@ -5380,7 +5404,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) {
@@ -5395,7 +5419,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);
}
}
@@ -5611,8 +5635,8 @@
@Override
public ComponentName getHomeActivityForUser(int userId) {
synchronized (mGlobalLock) {
- ActivityRecord homeActivity =
- mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId);
+ final ActivityRecord homeActivity =
+ mRootActivityContainer.getDefaultDisplayHomeActivityForUser(userId);
return homeActivity == null ? null : homeActivity.realActivity;
}
}
@@ -5650,14 +5674,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);
}
}
@@ -5738,7 +5762,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;
}
@@ -5747,7 +5771,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.
@@ -5764,7 +5788,7 @@
public void notifyKeyguardTrustedChanged() {
synchronized (mGlobalLock) {
if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
}
}
@@ -5791,7 +5815,7 @@
"setFocusedActivity: No activity record matching token=" + token);
}
if (r.moveFocusableActivityToTop("setFocusedActivity")) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
}
@@ -5942,7 +5966,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);
@@ -6049,7 +6073,7 @@
@Override
public void onPackageReplaced(ApplicationInfo aInfo) {
synchronized (mGlobalLock) {
- mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+ mRootActivityContainer.updateActivityApplicationInfo(aInfo);
}
}
@@ -6079,7 +6103,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) {
@@ -6162,14 +6186,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);
}
}
@@ -6233,7 +6257,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();
@@ -6245,12 +6269,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();
@@ -6279,7 +6303,7 @@
}
mWindowManager.closeSystemDialogs(reason);
- mStackSupervisor.closeSystemDialogsLocked();
+ mRootActivityContainer.closeSystemDialogs();
}
// Call into AM outside the synchronized block.
mAmInternal.broadcastCloseSystemDialogs(reason);
@@ -6293,9 +6317,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();
}
@@ -6312,7 +6336,7 @@
boolean didSomething =
getActivityStartController().clearPendingActivityLaunches(packageName);
- didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName,
+ didSomething |= mRootActivityContainer.finishDisabledPackageActivities(packageName,
null, doit, evenPersistent, userId);
return didSomething;
}
@@ -6321,7 +6345,7 @@
@Override
public void resumeTopActivities(boolean scheduleIdle) {
synchronized (mGlobalLock) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
if (scheduleIdle) {
mStackSupervisor.scheduleIdleLocked();
}
@@ -6338,7 +6362,7 @@
@Override
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
synchronized (mGlobalLock) {
- return mStackSupervisor.attachApplicationLocked(wpc);
+ return mRootActivityContainer.attachApplication(wpc);
}
}
@@ -6360,7 +6384,7 @@
// Showing launcher to avoid user entering credential twice.
startHomeActivity(currentUserId, "notifyLockedProfile");
}
- mStackSupervisor.lockAllProfileTasks(userId);
+ mRootActivityContainer.lockAllProfileTasks(userId);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -6381,7 +6405,7 @@
ActivityOptions activityOptions = options != null
? new ActivityOptions(options) : ActivityOptions.makeBasic();
final ActivityRecord homeActivity =
- mStackSupervisor.getDefaultDisplayHomeActivity();
+ mRootActivityContainer.getDefaultDisplayHomeActivity();
if (homeActivity != null) {
activityOptions.setLaunchTaskId(homeActivity.getTask().taskId);
}
@@ -6398,7 +6422,7 @@
synchronized (mGlobalLock) {
// The output proto of "activity --proto activities"
// is ActivityManagerServiceDumpActivitiesProto
- mStackSupervisor.writeToProto(proto,
+ mRootActivityContainer.writeToProto(proto,
ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
}
}
@@ -6493,7 +6517,7 @@
}
if (dumpPackage == null) {
pw.println(" mGlobalConfiguration: " + getGlobalConfiguration());
- mStackSupervisor.dumpDisplayConfigs(pw, " ");
+ mRootActivityContainer.dumpDisplayConfigs(pw, " ");
}
if (dumpAll) {
if (dumpPackage == null) {
@@ -6521,7 +6545,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);
@@ -6648,14 +6672,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;
}
}
@@ -6663,8 +6687,8 @@
@Override
public void rankTaskLayersIfNeeded() {
synchronized (mGlobalLock) {
- if (mStackSupervisor != null) {
- mStackSupervisor.rankTaskLayersIfNeeded();
+ if (mRootActivityContainer != null) {
+ mRootActivityContainer.rankTaskLayersIfNeeded();
}
}
}
@@ -6672,35 +6696,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,4 +6895,4 @@
}
}
}
-}
\ No newline at end of file
+}
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 94a47dd..bf00ffb 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -51,7 +51,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
-import android.app.WindowConfiguration;
import android.os.Trace;
import android.util.ArraySet;
import android.util.Slog;
@@ -83,7 +82,7 @@
AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
- mWallpaperControllerLocked = new WallpaperController(mService);
+ mWallpaperControllerLocked = mDisplayContent.mWallpaperController;
}
/**
@@ -97,16 +96,17 @@
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
- int transit = mDisplayContent.mAppTransition.getAppTransition();
+ final AppTransition appTransition = mDisplayContent.mAppTransition;
+ int transit = appTransition.getAppTransition();
if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
transit = WindowManager.TRANSIT_UNSET;
}
mDisplayContent.mSkipAppTransitionAnimation = false;
mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
- mDisplayContent.mAppTransition.removeAppTransitionTimeoutCallbacks();
+ appTransition.removeAppTransitionTimeoutCallbacks();
- mService.mRoot.mWallpaperMayChange = false;
+ mDisplayContent.mWallpaperMayChange = false;
int i;
for (i = 0; i < appsCount; i++) {
@@ -121,7 +121,7 @@
// Adjust wallpaper before we pull the lower/upper target, since pending changes
// (like the clearAnimatingFlags() above) might affect wallpaper target result.
// Or, the opening app window should be a wallpaper target.
- mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(mDisplayContent,
+ mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
mDisplayContent.mOpeningApps);
// Determine if closing and opening app token sets are wallpaper targets, in which case
@@ -142,7 +142,7 @@
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps);
- final boolean allowAnimations = mService.mPolicy.allowAppAnimationsLw();
+ final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw();
final AppWindowToken animLpToken = allowAnimations
? findAnimLayoutParamsToken(transit, activityTypes)
: null;
@@ -166,15 +166,15 @@
handleClosingApps(transit, animLp, voiceInteraction);
handleOpeningApps(transit, animLp, voiceInteraction);
- mDisplayContent.mAppTransition.setLastAppTransition(transit, topOpeningApp,
+ appTransition.setLastAppTransition(transit, topOpeningApp,
topClosingApp);
- final int flags = mDisplayContent.mAppTransition.getTransitFlags();
- layoutRedo = mDisplayContent.mAppTransition.goodToGo(transit, topOpeningApp,
+ final int flags = appTransition.getTransitFlags();
+ layoutRedo = appTransition.goodToGo(transit, topOpeningApp,
topClosingApp, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps);
handleNonAppWindowsInTransition(transit, flags);
- mDisplayContent.mAppTransition.postAnimationCallback();
- mDisplayContent.mAppTransition.clear();
+ appTransition.postAnimationCallback();
+ appTransition.clear();
} finally {
mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
@@ -255,8 +255,8 @@
}
/**
- * @return The set of {@link WindowConfiguration.ActivityType}s contained in the set of apps in
- * {@code array1} and {@code array2}.
+ * @return The set of {@link android.app.WindowConfiguration.ActivityType}s contained in the set
+ * of apps in {@code array1} and {@code array2}.
*/
private static ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1,
ArraySet<AppWindowToken> array2) {
@@ -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/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..b9b9d31 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;
@@ -65,7 +65,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())
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 92944a0..df81c07 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;
@@ -461,8 +598,10 @@
forAllWindows(mService::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();
@@ -499,10 +638,9 @@
mService.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()) {
@@ -596,11 +734,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,7 +770,7 @@
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);
@@ -656,8 +789,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
@@ -768,9 +901,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 +957,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 +974,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();
}
}
@@ -1021,6 +1148,10 @@
@Override
void removeChild(WindowState child) {
+ if (!mChildren.contains(child)) {
+ // This can be true when testing.
+ return;
+ }
super.removeChild(child);
checkKeyguardFlagsChanged();
updateLetterboxSurface(child);
@@ -1042,6 +1173,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) {
@@ -1081,8 +1226,12 @@
super.onDisplayChanged(dc);
if (prevDc != null && prevDc.mFocusedApp == this) {
prevDc.setFocusedApp(null);
- if (dc.getTopStack().getTopChild().getTopChild() == this) {
- dc.setFocusedApp(this);
+ final TaskStack stack = dc.getTopStack();
+ if (stack != null) {
+ final Task task = stack.getTopChild();
+ if (task != null && task.getTopChild() == this) {
+ dc.setFocusedApp(this);
+ }
}
}
}
@@ -1296,9 +1445,7 @@
startingData = fromToken.startingData;
fromToken.startingData = null;
fromToken.startingMoved = true;
- if (getController() != null) {
- getController().scheduleAddStartingWindow();
- }
+ scheduleAddStartingWindow();
return true;
}
@@ -1467,6 +1614,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.
*
@@ -1529,8 +1680,8 @@
}
}
} else if (w.isDrawnLw()) {
- if (getController() != null) {
- getController().reportStartingWindowDrawn();
+ if (mActivityRecord != null) {
+ mActivityRecord.onStartingWindowDrawn(SystemClock.uptimeMillis());
}
startingDisplayed = true;
}
@@ -1597,6 +1748,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 =
+ mService.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,
+ 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, "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(mService, 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(mService, 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 (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING");
+ mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+ }
+ }
+
+ private final Runnable mAddStartingWindow = new Runnable() {
+
+ @Override
+ public void run() {
+ synchronized (mService.mGlobalLock) {
+ // There can only be one adding request, silly caller!
+ mService.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 (mService.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.
+ 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);
+ }
+ });
+ }
+
@Override
boolean fillsParent() {
return mFillsParent;
@@ -1750,10 +2161,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;
}
@@ -2211,9 +2620,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/policy/BarController.java b/services/core/java/com/android/server/wm/BarController.java
similarity index 90%
rename from services/core/java/com/android/server/policy/BarController.java
rename to services/core/java/com/android/server/wm/BarController.java
index 14c985c..a335fa2 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.wm;
import static com.android.server.wm.BarControllerProto.STATE;
import static com.android.server.wm.BarControllerProto.TRANSIENT_STATE;
@@ -30,7 +30,7 @@
import android.view.WindowManager;
import com.android.server.LocalServices;
-import com.android.server.policy.WindowManagerPolicy.WindowState;
+import com.android.server.UiThread;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
@@ -59,7 +59,7 @@
private final int mTranslucentWmFlag;
protected final Handler mHandler;
private final Object mServiceAquireLock = new Object();
- protected StatusBarManagerInternal mStatusBarInternal;
+ private StatusBarManagerInternal mStatusBarInternal;
protected WindowState mWin;
private int mState = StatusBarManager.WINDOW_STATE_SHOWING;
@@ -73,7 +73,7 @@
private OnBarVisibilityChangedListener mVisibilityChangeListener;
- public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag,
+ BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag,
int statusBarManagerId, int translucentWmFlag, int transparentFlag) {
mTag = "BarController." + tag;
mTransientFlag = transientFlag;
@@ -85,7 +85,7 @@
mHandler = new BarHandler();
}
- public void setWindow(WindowState win) {
+ void setWindow(WindowState win) {
mWin = win;
}
@@ -94,11 +94,11 @@
*
* This is used to determine if letterboxes interfere with the display of such content.
*/
- public void setContentFrame(Rect frame) {
+ void setContentFrame(Rect frame) {
mContentFrame.set(frame);
}
- public void setShowTransparent(boolean transparent) {
+ void setShowTransparent(boolean transparent) {
if (transparent != mShowTransparent) {
mShowTransparent = transparent;
mSetUnHideFlagWhenNextTransparent = transparent;
@@ -106,27 +106,27 @@
}
}
- public void showTransient() {
+ void showTransient() {
if (mWin != null) {
setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
}
}
- public boolean isTransientShowing() {
+ boolean isTransientShowing() {
return mTransientBarState == TRANSIENT_BAR_SHOWING;
}
- public boolean isTransientShowRequested() {
+ boolean isTransientShowRequested() {
return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED;
}
- public boolean wasRecentlyTranslucent() {
+ boolean wasRecentlyTranslucent() {
return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS;
}
- public void adjustSystemUiVisibilityLw(int oldVis, int vis) {
- if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING &&
- (vis & mTransientFlag) == 0) {
+ void adjustSystemUiVisibilityLw(int oldVis, int vis) {
+ if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING
+ && (vis & mTransientFlag) == 0) {
// sysui requests hide
setTransientBarState(TRANSIENT_BAR_HIDING);
setBarShowingLw(false);
@@ -136,7 +136,7 @@
}
}
- public int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
+ int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
if (mWin != null) {
if (win != null && (win.getAttrs().privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) {
@@ -164,7 +164,7 @@
return win == null || !win.isLetterboxedOverlappingWith(mContentFrame);
}
- public boolean setBarShowingLw(final boolean show) {
+ boolean setBarShowingLw(final boolean show) {
if (mWin == null) return false;
if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
mPendingShow = true;
@@ -227,7 +227,7 @@
public void run() {
StatusBarManagerInternal statusbar = getStatusBarInternal();
if (statusbar != null) {
- statusbar.setWindowState(mStatusBarManagerId, state);
+ statusbar.setWindowState(mWin.getDisplayId(), mStatusBarManagerId, state);
}
}
});
@@ -236,7 +236,7 @@
return false;
}
- public boolean checkHiddenLw() {
+ boolean checkHiddenLw() {
if (mWin != null && mWin.isDrawnLw()) {
if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) {
updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN);
@@ -254,7 +254,7 @@
return false;
}
- public boolean checkShowTransientBarLw() {
+ boolean checkShowTransientBarLw() {
if (mTransientBarState == TRANSIENT_BAR_SHOWING) {
if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown");
return false;
@@ -272,7 +272,7 @@
}
}
- public int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
+ int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
if (mWin == null) return vis;
if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested
if (transientAllowed) {
@@ -296,8 +296,8 @@
vis |= mTransientFlag; // ignore clear requests until transition completes
vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile
}
- if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 ||
- ((vis | oldVis) & mTransparentFlag) != 0) {
+ if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0
+ || ((vis | oldVis) & mTransparentFlag) != 0) {
mLastTranslucent = SystemClock.uptimeMillis();
}
return vis;
@@ -330,14 +330,14 @@
throw new IllegalArgumentException("Unknown state " + state);
}
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(STATE, mState);
proto.write(TRANSIENT_STATE, mTransientBarState);
proto.end(token);
}
- public void dump(PrintWriter pw, String prefix) {
+ void dump(PrintWriter pw, String prefix) {
if (mWin != null) {
pw.print(prefix); pw.println(mTag);
pw.print(prefix); pw.print(" "); pw.print("mState"); pw.print('=');
@@ -349,6 +349,10 @@
}
private class BarHandler extends Handler {
+ BarHandler() {
+ super(UiThread.getHandler().getLooper());
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
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..fa3c7ca 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -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) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ab7d259..c0e9836 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -34,6 +34,7 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.View.GONE;
+import static android.view.InsetsState.TYPE_IME;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_TOP;
@@ -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;
@@ -124,6 +124,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,16 +152,19 @@
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;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
+import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
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;
@@ -182,9 +186,6 @@
/**
* Utility class for keeping track of the WindowStates and other pertinent contents of a
* particular Display.
- *
- * IMPORTANT: No method from this class should ever be used without holding
- * WindowManagerService.mWindowMap.
*/
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
implements WindowManagerPolicy.DisplayContentInfo {
@@ -289,7 +290,8 @@
* @see WindowManagerService#createWatermarkInTransaction()
*/
final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
- /** @see #computeCompatSmallestWidth(boolean, int, int, int, int) */
+
+ /** @see #computeCompatSmallestWidth(boolean, int, int, int, DisplayCutout) */
private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
/**
@@ -353,6 +355,14 @@
int pendingLayoutChanges;
int mDeferredRotationPauseCount;
+ /**
+ * Used to gate application window layout until we have sent the complete configuration.
+ * TODO: There are still scenarios where we may be out of sync with the client. Ideally
+ * we want to replace this flag with a mechanism that will confirm the configuration
+ * applied by the client is the one expected by the system server.
+ */
+ boolean mWaitingForConfig;
+
// TODO(multi-display): remove some of the usages.
@VisibleForTesting
boolean isDefaultDisplay;
@@ -414,6 +424,8 @@
WallpaperController mWallpaperController;
+ boolean mWallpaperMayChange = false;
+
private final SurfaceSession mSession = new SurfaceSession();
/**
@@ -461,14 +473,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;
@@ -506,6 +510,13 @@
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.
+ private int mLastDispatchedSystemUiVisibility = 0;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final AppWindowToken atoken = w.mAppToken;
@@ -647,7 +658,7 @@
w.mLayoutNeeded = false;
w.prelayout();
final boolean firstLayout = !w.isLaidOut();
- mService.mPolicy.layoutWindowLw(w, null, mDisplayFrames);
+ getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
// If this is the first layout, we need to initialize the last inset values as
@@ -686,7 +697,7 @@
}
w.mLayoutNeeded = false;
w.prelayout();
- mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
+ getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
+ " mContainingFrame=" + w.getContainingFrame()
@@ -706,7 +717,7 @@
};
private final Consumer<WindowState> mApplyPostLayoutPolicy =
- w -> mService.mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow(),
+ w -> getDisplayPolicy().applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow(),
mInputMethodTarget);
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
@@ -751,8 +762,7 @@
}
}
- if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
- && mWallpaperController.isWallpaperTarget(w)) {
+ if (obscuredChanged && w.isVisibleLw() && mWallpaperController.isWallpaperTarget(w)) {
// This is the wallpaper target and its obscured state changed... make sure the
// current wallpaper's visibility has been updated accordingly.
mWallpaperController.updateWallpaperVisibility();
@@ -784,7 +794,7 @@
if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"First draw done in potential wallpaper target " + w);
- root.mWallpaperMayChange = true;
+ mWallpaperMayChange = true;
pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats(
@@ -816,11 +826,10 @@
* initialize direct children.
* @param display May not be null.
* @param service You know.
- * @param wallpaperController wallpaper windows controller used to adjust the positioning of the
- * wallpaper windows in the window list.
+ * @param controller The controller for the display container.
*/
DisplayContent(Display display, WindowManagerService service,
- WallpaperController wallpaperController, DisplayWindowController controller) {
+ DisplayWindowController controller) {
super(service);
setController(controller);
if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
@@ -831,22 +840,13 @@
mDisplay = display;
mDisplayId = display.getDisplayId();
- mWallpaperController = wallpaperController;
+ mWallpaperController = new WallpaperController(mService, this);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
initializeDisplayBaseInfo();
- mDisplayPolicy = new DisplayPolicy(service);
- mDisplayRotation = new DisplayRotation(service, this);
- 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);
- }
- mDividerControllerLocked = new DockedStackDividerController(service, this);
- mPinnedStackControllerLocked = new PinnedStackController(service, this);
mAppTransition = new AppTransition(service.mContext, service, this);
mAppTransition.registerListenerLocked(service.mActivityManagerAppTransitionNotifier);
@@ -857,18 +857,31 @@
mBoundsAnimationController = new BoundsAnimationController(service.mContext,
mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler);
- // 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;
+ if (mService.mInputManager != null) {
+ final InputChannel inputChannel = mService.mInputManager.monitorInput("Display "
+ + mDisplayId, mDisplayId);
+ mPointerEventDispatcher = inputChannel != null
+ ? new PointerEventDispatcher(inputChannel) : null;
+ } else {
+ mPointerEventDispatcher = null;
+ }
+ mDisplayPolicy = new DisplayPolicy(service, this);
+ mDisplayRotation = new DisplayRotation(service, this);
+ 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);
+ }
+ if (mService.mDisplayReady) {
+ mDisplayPolicy.onConfigurationChanged();
+ }
+ if (mService.mSystemReady) {
+ mDisplayPolicy.systemReady();
+ }
+ mDividerControllerLocked = new DockedStackDividerController(service, this);
+ mPinnedStackControllerLocked = new PinnedStackController(service, this);
- final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
- .setSize(mSurfaceSize, mSurfaceSize)
- .setOpaque(true);
+ final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession).setOpaque(true);
mWindowingLayer = b.setName("Display Root").build();
mOverlayLayer = b.setName("Display Overlays").build();
@@ -895,15 +908,7 @@
mService.mAnimator.addDisplayLocked(mDisplayId);
mInputMonitor = new InputMonitor(service, mDisplayId);
-
- if (mService.mInputManager != null) {
- final InputChannel inputChannel = mService.mInputManager.monitorInput("Display "
- + mDisplayId, mDisplayId);
- mPointerEventDispatcher = inputChannel != null
- ? new PointerEventDispatcher(inputChannel) : null;
- } else {
- mPointerEventDispatcher = null;
- }
+ mInsetsStateController = new InsetsStateController(this);
}
boolean isReady() {
@@ -1040,6 +1045,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;
@@ -1223,7 +1245,7 @@
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
+ mDisplayId + " based on lastOrientation=" + lastOrientation
+ " and oldRotation=" + oldRotation);
- boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(mDisplayRotation,
+ boolean mayRotateSeamlessly = mDisplayPolicy.shouldRotateSeamlessly(mDisplayRotation,
oldRotation, rotation);
if (mayRotateSeamlessly) {
@@ -1274,7 +1296,7 @@
+ (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
- mService.mWaitingForConfig = true;
+ mWaitingForConfig = true;
}
mRotation = rotation;
@@ -1286,7 +1308,7 @@
setLayoutNeeded();
final int[] anim = new int[2];
- mService.mPolicy.selectRotationAnimationLw(anim);
+ mDisplayPolicy.selectRotationAnimationLw(anim);
if (!rotateSeamlessly) {
mService.startFreezingDisplayLocked(anim[0], anim[1], this);
@@ -1445,10 +1467,10 @@
final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(mRotation);
final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
- final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode,
- mDisplayId, displayCutout);
- final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode,
- mDisplayId, displayCutout);
+ final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode,
+ displayCutout);
+ final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode,
+ displayCutout);
mDisplayInfo.rotation = mRotation;
mDisplayInfo.logicalWidth = dw;
mDisplayInfo.logicalHeight = dh;
@@ -1527,13 +1549,13 @@
final float density = mDisplayMetrics.density;
config.screenWidthDp =
- (int)(mService.mPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation,
- config.uiMode, mDisplayId, displayInfo.displayCutout) / density);
+ (int)(mDisplayPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation,
+ config.uiMode, displayInfo.displayCutout) / density);
config.screenHeightDp =
- (int)(mService.mPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation,
- config.uiMode, mDisplayId, displayInfo.displayCutout) / density);
+ (int)(mDisplayPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation,
+ config.uiMode, displayInfo.displayCutout) / density);
- mService.mPolicy.getNonDecorInsetsLw(displayInfo.rotation, dw, dh,
+ mDisplayPolicy.getNonDecorInsetsLw(displayInfo.rotation, dw, dh,
displayInfo.displayCutout, mTmpRect);
final int leftInset = mTmpRect.left;
final int topInset = mTmpRect.top;
@@ -1544,8 +1566,8 @@
final boolean rotated = (displayInfo.rotation == Surface.ROTATION_90
|| displayInfo.rotation == Surface.ROTATION_270);
- computeSizeRangesAndScreenLayout(displayInfo, mDisplayId, rotated, config.uiMode, dw, dh,
- density, config);
+ computeSizeRangesAndScreenLayout(displayInfo, rotated, config.uiMode, dw, dh, density,
+ config);
config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
| ((displayInfo.flags & Display.FLAG_ROUND) != 0
@@ -1555,7 +1577,7 @@
config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, config.uiMode, dw,
- dh, displayInfo.displayCutout, mDisplayId);
+ dh, displayInfo.displayCutout);
config.densityDpi = displayInfo.logicalDensityDpi;
config.colorMode =
@@ -1624,6 +1646,8 @@
mService.mH.sendEmptyMessage(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
}
+ mDisplayPolicy.updateConfigurationDependentBehaviors();
+
// Let the policy update hidden states.
config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
@@ -1632,7 +1656,7 @@
}
private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
- DisplayCutout displayCutout, int displayId) {
+ DisplayCutout displayCutout) {
mTmpDisplayMetrics.setTo(mDisplayMetrics);
final DisplayMetrics tmpDm = mTmpDisplayMetrics;
final int unrotDw, unrotDh;
@@ -1644,22 +1668,22 @@
unrotDh = dh;
}
int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh,
- displayCutout, displayId);
+ displayCutout);
sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw,
- displayCutout, displayId);
+ displayCutout);
sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh,
- displayCutout, displayId);
+ displayCutout);
sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw,
- displayCutout, displayId);
+ displayCutout);
return sw;
}
private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
- DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout, int displayId) {
- dm.noncompatWidthPixels = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
- displayId, displayCutout);
- dm.noncompatHeightPixels = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
- uiMode, displayId, displayCutout);
+ DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout) {
+ dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
+ displayCutout);
+ dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
+ displayCutout);
float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
if (curSize == 0 || size < curSize) {
@@ -1668,8 +1692,8 @@
return curSize;
}
- private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, int displayId,
- boolean rotated, int uiMode, int dw, int dh, float density, Configuration outConfig) {
+ private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, boolean rotated,
+ int uiMode, int dw, int dh, float density, Configuration outConfig) {
// We need to determine the smallest width that will occur under normal
// operation. To this, start with the base screen size and compute the
@@ -1687,34 +1711,28 @@
displayInfo.smallestNominalAppHeight = 1<<30;
displayInfo.largestNominalAppWidth = 0;
displayInfo.largestNominalAppHeight = 0;
- adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_0, uiMode, unrotDw,
- unrotDh);
- adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_90, uiMode, unrotDh,
- unrotDw);
- adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_180, uiMode, unrotDw,
- unrotDh);
- adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_270, uiMode, unrotDh,
- unrotDw);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, uiMode, unrotDw, unrotDh);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, uiMode, unrotDh, unrotDw);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, uiMode, unrotDw, unrotDh);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, uiMode, unrotDh, unrotDw);
int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
- displayInfo.displayCutout, displayId);
+ displayInfo.displayCutout);
sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode,
- displayInfo.displayCutout, displayId);
+ displayInfo.displayCutout);
sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode,
- displayInfo.displayCutout, displayId);
+ displayInfo.displayCutout);
sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode,
- displayInfo.displayCutout, displayId);
+ displayInfo.displayCutout);
outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
outConfig.screenLayout = sl;
}
private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh,
- int uiMode, DisplayCutout displayCutout, int displayId) {
+ int uiMode, DisplayCutout displayCutout) {
// Get the app screen size at this rotation.
- int w = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayId,
- displayCutout);
- int h = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayId,
- displayCutout);
+ int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
+ int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout);
// Compute the screen layout size class for this rotation.
int longSize = w;
@@ -1729,20 +1747,20 @@
return Configuration.reduceScreenLayout(curLayout, longSize, shortSize);
}
- private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int displayId, int rotation,
+ private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation,
int uiMode, int dw, int dh) {
final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
rotation).getDisplayCutout();
- final int width = mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode,
- displayId, displayCutout);
+ final int width = mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode,
+ displayCutout);
if (width < displayInfo.smallestNominalAppWidth) {
displayInfo.smallestNominalAppWidth = width;
}
if (width > displayInfo.largestNominalAppWidth) {
displayInfo.largestNominalAppWidth = width;
}
- final int height = mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode,
- displayId, displayCutout);
+ final int height = mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode,
+ displayCutout);
if (height < displayInfo.smallestNominalAppHeight) {
displayInfo.smallestNominalAppHeight = height;
}
@@ -1887,6 +1905,9 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
super.onConfigurationChanged(newParentConfig);
+ if (mDisplayPolicy != null) {
+ mDisplayPolicy.onConfigurationChanged();
+ }
// If there was no pinned stack, we still need to notify the controller of the display info
// update as a result of the config change.
@@ -2384,7 +2405,7 @@
mRemovingDisplay = false;
}
- mInputMonitor.onRemoved();
+ mDisplayPolicy.onDisplayRemoved();
mService.mWindowPlacerLocked.requestTraversal();
}
@@ -2595,7 +2616,6 @@
}
mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
mAppTransition.writeToProto(proto, APP_TRANSITION);
- proto.write(SURFACE_SIZE, mSurfaceSize);
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
@@ -2659,6 +2679,13 @@
}
}
pw.print(" mFocusedApp="); pw.println(mFocusedApp);
+ if (mLastStatusBarVisibility != 0) {
+ pw.print(" mLastStatusBarVisibility=0x");
+ pw.println(Integer.toHexString(mLastStatusBarVisibility));
+ }
+
+ pw.println();
+ mWallpaperController.dump(pw, " ");
pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
@@ -2709,6 +2736,8 @@
mDisplayRotation.dump(prefix, pw);
pw.println();
mInputMonitor.dump(pw, " ");
+ pw.println();
+ mInsetsStateController.dump(prefix, pw);
}
@Override
@@ -2849,9 +2878,7 @@
}
}
- // System UI is only shown on the default display.
- int focusChanged = isDefaultDisplay
- ? mService.mPolicy.focusChangedLw(oldFocus, newFocus) : 0;
+ int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
if (imWindowChanged && oldFocus != mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed.
@@ -2993,6 +3020,8 @@
mInputMethodWindow.getDisplayId());
}
computeImeTarget(true /* updateImeTarget */);
+ mInsetsStateController.getSourceProvider(TYPE_IME).setWindow(win,
+ null /* frameProvider */);
}
/**
@@ -3318,6 +3347,31 @@
return win != null;
}
+ void statusBarVisibilityChanged(int visibility) {
+ mLastStatusBarVisibility = visibility;
+ visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(visibility);
+ updateStatusBarVisibilityLocked(visibility);
+ }
+
+ private boolean updateStatusBarVisibilityLocked(int visibility) {
+ if (mLastDispatchedSystemUiVisibility == visibility) {
+ return false;
+ }
+ final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility)
+ // We are only interested in differences of one of the
+ // clearable flags...
+ & View.SYSTEM_UI_CLEARABLE_FLAGS
+ // ...if it has actually been cleared.
+ & ~visibility;
+
+ mLastDispatchedSystemUiVisibility = visibility;
+ if (isDefaultDisplay) {
+ mService.mInputManager.setSystemUiVisibility(visibility);
+ }
+ updateSystemUiVisibility(visibility, globalDiff);
+ return true;
+ }
+
void updateSystemUiVisibility(int visibility, int globalDiff) {
forAllWindows(w -> {
try {
@@ -3338,6 +3392,13 @@
}, true /* traverseTopToBottom */);
}
+ void reevaluateStatusBarVisibility() {
+ int visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
+ if (updateStatusBarVisibilityLocked(visibility)) {
+ mService.mWindowPlacerLocked.requestTraversal();
+ }
+ }
+
void onWindowFreezeTimeout() {
Slog.w(TAG_WM, "Window freeze timeout expired.");
mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
@@ -3369,9 +3430,6 @@
// TODO: Super crazy long method that should be broken down...
void applySurfaceChangesTransaction(boolean recoveringMemory) {
-
- final int dw = mDisplayInfo.logicalWidth;
- final int dh = mDisplayInfo.logicalHeight;
final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
mTmpUpdateAllDrawn.clear();
@@ -3388,12 +3446,8 @@
if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("On entry to LockedInner",
pendingLayoutChanges);
- // TODO(multi-display): For now adjusting wallpaper only on primary display to avoid
- // the wallpaper window jumping across displays.
- // Remove check for default display when there will be support for multiple wallpaper
- // targets (on different displays).
- if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- mWallpaperController.adjustWallpaperWindows(this);
+ if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ mWallpaperController.adjustWallpaperWindows();
}
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
@@ -3418,13 +3472,12 @@
// FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think it is animating.
pendingLayoutChanges = 0;
- if (isDefaultDisplay) {
- mService.mPolicy.beginPostLayoutPolicyLw(dw, dh);
- forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
- pendingLayoutChanges |= mService.mPolicy.finishPostLayoutPolicyLw();
- if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
- "after finishPostLayoutPolicyLw", pendingLayoutChanges);
- }
+ mDisplayPolicy.beginPostLayoutPolicyLw();
+ forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
+ pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
+ if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
+ "after finishPostLayoutPolicyLw", pendingLayoutChanges);
+ mInsetsStateController.onPostLayout();
} while (pendingLayoutChanges != 0);
mTmpApplySurfaceChangesTransactionState.reset();
@@ -3492,10 +3545,6 @@
}
}
- int getSurfaceSize() {
- return mSurfaceSize;
- }
-
void performLayout(boolean initial, boolean updateInputWindows) {
if (!isLayoutNeeded()) {
return;
@@ -3514,12 +3563,7 @@
// TODO: Not sure if we really need to set the rotation here since we are updating from the
// display info above...
mDisplayFrames.mRotation = mRotation;
- mService.mPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
- if (isDefaultDisplay) {
- // Not needed on non-default displays.
- mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
- mService.mScreenRect.set(0, 0, dw, dh);
- }
+ mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
int seq = mLayoutSeq + 1;
if (seq < 0) seq = 0;
@@ -3643,6 +3687,7 @@
}
mTmpWindow = w;
w.setDisplayLayoutNeeded();
+ w.finishSeamlessRotation(true /* timeout */);
mService.markForSeamlessRotation(w, false);
}, true /* traverseTopToBottom */);
@@ -4442,8 +4487,6 @@
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
SurfaceSession s = child != null ? child.getSession() : getSession();
final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s);
- b.setSize(mSurfaceSize, mSurfaceSize);
-
if (child == null) {
return b;
}
@@ -4549,7 +4592,7 @@
* However we need child windows of the applications to be above the IME (Text drag handles).
* This is a non-strictly hierarcical layering and we need to break out of the Z ordering
* somehow. We do this by relatively ordering children of the target to the IME in cooperation
- * with {@link #WindowState#assignLayer}
+ * with {@link WindowState#assignLayer}
*/
void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) {
child.assignRelativeLayer(t, mImeWindowsContainers.getSurfaceControl(), 1);
@@ -4673,7 +4716,7 @@
Slog.v(TAG_WM, "Wallpaper layer changed: assigning layers + relayout");
}
computeImeTarget(true /* updateImeTarget */);
- mService.mRoot.mWallpaperMayChange = true;
+ 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;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 9151ddf..0e5947a 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -16,20 +16,145 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+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.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;
+import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+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;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
+import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
+
+import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_HIDE;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
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.localLOGV;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.StatusBarManager;
+import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.hardware.input.InputManager;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.PrintWriterPrinter;
import android.util.Slog;
+import android.view.DisplayCutout;
+import android.view.Gravity;
+import android.view.IApplicationToken;
+import android.view.InputChannel;
+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;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.view.WindowManagerGlobal;
+import android.view.WindowManagerPolicyConstants;
+import android.view.accessibility.AccessibilityManager;
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ScreenShapeHelper;
+import com.android.internal.util.ScreenshotHelper;
+import com.android.server.LocalServices;
+import com.android.server.UiThread;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.policy.WindowManagerPolicy.InputConsumer;
+import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition;
import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
+import com.android.server.policy.WindowOrientationListener;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
@@ -38,12 +163,61 @@
*/
public class DisplayPolicy {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM;
+ private static final boolean DEBUG = false;
+
+ private static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false;
+
+ // The panic gesture may become active only after the keyguard is dismissed and the immersive
+ // app shows again. If that doesn't happen for 30s we drop the gesture.
+ private static final long PANIC_GESTURE_EXPIRATION = 30000;
+
+ // Controls navigation bar opacity depending on which workspace stacks are currently
+ // visible.
+ // Nav bar is always opaque when either the freeform stack or docked stack is visible.
+ private static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0;
+ // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque.
+ private static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 1;
+
+ /**
+ * These are the system UI flags that, when changing, can cause the layout
+ * of the screen to change.
+ */
+ private static final int SYSTEM_UI_CHANGING_LAYOUT =
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.STATUS_BAR_TRANSLUCENT
+ | View.NAVIGATION_BAR_TRANSLUCENT
+ | View.STATUS_BAR_TRANSPARENT
+ | View.NAVIGATION_BAR_TRANSPARENT;
private final WindowManagerService mService;
+ private final Context mContext;
+ private final DisplayContent mDisplayContent;
private final Object mLock;
+ private final Handler mHandler;
private final boolean mCarDockEnablesAccelerometer;
private final boolean mDeskDockEnablesAccelerometer;
+ private final boolean mTranslucentDecorEnabled;
+ private final AccessibilityManager mAccessibilityManager;
+ private final ImmersiveModeConfirmation mImmersiveModeConfirmation;
+ private final ScreenshotHelper mScreenshotHelper;
+
+ private final Object mServiceAcquireLock = new Object();
+ private StatusBarManagerInternal mStatusBarManagerInternal;
+
+ private StatusBarManagerInternal getStatusBarManagerInternal() {
+ synchronized (mServiceAcquireLock) {
+ if (mStatusBarManagerInternal == null) {
+ mStatusBarManagerInternal =
+ LocalServices.getService(StatusBarManagerInternal.class);
+ }
+ return mStatusBarManagerInternal;
+ }
+ }
+
+ @VisibleForTesting
+ private final SystemGesturesPointerEventListener mSystemGestures;
private volatile int mLidState = LID_ABSENT;
private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@@ -64,32 +238,311 @@
private volatile boolean mKeyguardDrawComplete;
private volatile boolean mWindowManagerDrawComplete;
- DisplayPolicy(WindowManagerService service) {
+ private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>();
+ private WindowState mStatusBar = null;
+ private final int[] mStatusBarHeightForRotation = new int[4];
+ private WindowState mNavigationBar = null;
+ @NavigationBarPosition
+ private int mNavigationBarPosition = NAV_BAR_BOTTOM;
+ private int[] mNavigationBarHeightForRotationDefault = new int[4];
+ private int[] mNavigationBarWidthForRotationDefault = new int[4];
+ private int[] mNavigationBarHeightForRotationInCarMode = new int[4];
+ private int[] mNavigationBarWidthForRotationInCarMode = new int[4];
+
+ private final StatusBarController mStatusBarController = new StatusBarController();
+
+ private final BarController mNavigationBarController = new BarController("NavigationBar",
+ View.NAVIGATION_BAR_TRANSIENT,
+ View.NAVIGATION_BAR_UNHIDE,
+ View.NAVIGATION_BAR_TRANSLUCENT,
+ StatusBarManager.WINDOW_NAVIGATION_BAR,
+ FLAG_TRANSLUCENT_NAVIGATION,
+ View.NAVIGATION_BAR_TRANSPARENT);
+
+ private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
+ new BarController.OnBarVisibilityChangedListener() {
+ @Override
+ public void onBarVisibilityChanged(boolean visible) {
+ if (mAccessibilityManager == null) {
+ return;
+ }
+ mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible);
+ }
+ };
+
+ // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+ private NavigationBarExperiments mExperiments = new NavigationBarExperiments();
+ // EXPERIMENT END
+
+ @GuardedBy("mHandler")
+ private SleepToken mDreamingSleepToken;
+
+ @GuardedBy("mHandler")
+ private SleepToken mWindowSleepToken;
+
+ private final Runnable mAcquireSleepTokenRunnable;
+ private final Runnable mReleaseSleepTokenRunnable;
+
+ // The windows we were told about in focusChanged.
+ private WindowState mFocusedWindow;
+ private WindowState mLastFocusedWindow;
+
+ IApplicationToken mFocusedApp;
+
+ int mLastSystemUiFlags;
+ // Bits that we are in the process of clearing, so we want to prevent
+ // them from being set by applications until everything has been updated
+ // to have them clear.
+ private int mResettingSystemUiFlags = 0;
+ // Bits that we are currently always keeping cleared.
+ private int mForceClearedSystemUiFlags = 0;
+ private int mLastFullscreenStackSysUiFlags;
+ private int mLastDockedStackSysUiFlags;
+ private final Rect mNonDockedStackBounds = new Rect();
+ private final Rect mDockedStackBounds = new Rect();
+ private final Rect mLastNonDockedStackBounds = new Rect();
+ private final Rect mLastDockedStackBounds = new Rect();
+
+ // What we last reported to system UI about whether the compatibility
+ // menu needs to be displayed.
+ private boolean mLastFocusNeedsMenu = false;
+ // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
+ private long mPendingPanicGestureUptime;
+
+ private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
+ private static final Rect sTmpRect = new Rect();
+ private static final Rect sTmpDockedFrame = new Rect();
+ private static final Rect sTmpNavFrame = new Rect();
+ private static final Rect sTmpLastParentFrame = new Rect();
+
+ private WindowState mTopFullscreenOpaqueWindowState;
+ private WindowState mTopFullscreenOpaqueOrDimmingWindowState;
+ private WindowState mTopDockedOpaqueWindowState;
+ private WindowState mTopDockedOpaqueOrDimmingWindowState;
+ private boolean mTopIsFullscreen;
+ private boolean mForceStatusBar;
+ private boolean mForceStatusBarFromKeyguard;
+ private boolean mForceStatusBarTransparent;
+ private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
+ private boolean mForcingShowNavBar;
+ private int mForcingShowNavBarLayer;
+ private boolean mForceShowSystemBars;
+
+ private boolean mShowingDream;
+ private boolean mLastShowingDream;
+ private boolean mDreamingLockscreen;
+ private boolean mDreamingSleepTokenNeeded;
+ private boolean mWindowSleepTokenNeeded;
+ private boolean mLastWindowSleepTokenNeeded;
+ private boolean mAllowLockscreenWhenOn;
+
+ private InputConsumer mInputConsumer = null;
+
+ // -------- PolicyHandler --------
+ private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1;
+ private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
+ private static final int MSG_DISPOSE_INPUT_CONSUMER = 3;
+
+ private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
+ private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
+
+ private class PolicyHandler extends Handler {
+
+ PolicyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_DREAMING_SLEEP_TOKEN:
+ updateDreamingSleepToken(msg.arg1 != 0);
+ break;
+ case MSG_REQUEST_TRANSIENT_BARS:
+ WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
+ ? mStatusBar : mNavigationBar;
+ if (targetBar != null) {
+ requestTransientBars(targetBar);
+ }
+ break;
+ case MSG_DISPOSE_INPUT_CONSUMER:
+ disposeInputConsumer((InputConsumer) msg.obj);
+ break;
+ }
+ }
+ }
+
+ DisplayPolicy(WindowManagerService service, DisplayContent displayContent) {
mService = service;
+ mContext = displayContent.isDefaultDisplay ? service.mContext
+ : service.mContext.createDisplayContext(displayContent.getDisplay());
+ mDisplayContent = displayContent;
mLock = service.getWindowManagerLock();
- mCarDockEnablesAccelerometer = service.mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_carDockEnablesAccelerometer);
- mDeskDockEnablesAccelerometer = service.mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_deskDockEnablesAccelerometer);
+
+ final Resources r = mContext.getResources();
+ mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
+ mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer);
+ mTranslucentDecorEnabled = r.getBoolean(R.bool.config_enableTranslucentDecor);
+ updateConfigurationDependentBehaviors();
+
+ mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ if (!displayContent.isDefaultDisplay) {
+ mAwake = true;
+ mScreenOnEarly = true;
+ mScreenOnFully = true;
+ }
+
+ final Looper looper = UiThread.getHandler().getLooper();
+ mHandler = new PolicyHandler(looper);
+ mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler,
+ new SystemGesturesPointerEventListener.Callbacks() {
+ @Override
+ public void onSwipeFromTop() {
+ if (mStatusBar != null) {
+ requestTransientBars(mStatusBar);
+ }
+ }
+
+ @Override
+ public void onSwipeFromBottom() {
+ if (mNavigationBar != null
+ && mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ requestTransientBars(mNavigationBar);
+ }
+ }
+
+ @Override
+ public void onSwipeFromRight() {
+ if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) {
+ requestTransientBars(mNavigationBar);
+ }
+ }
+
+ @Override
+ public void onSwipeFromLeft() {
+ if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) {
+ requestTransientBars(mNavigationBar);
+ }
+ }
+
+ @Override
+ public void onFling(int duration) {
+ if (mService.mPowerManagerInternal != null) {
+ mService.mPowerManagerInternal.powerHint(
+ PowerHint.INTERACTION, duration);
+ }
+ }
+
+ @Override
+ public void onDebug() {
+ // no-op
+ }
+
+ private WindowOrientationListener getOrientationListener() {
+ final DisplayRotation rotation = mDisplayContent.getDisplayRotation();
+ return rotation != null ? rotation.getOrientationListener() : null;
+ }
+
+ @Override
+ public void onDown() {
+ final WindowOrientationListener listener = getOrientationListener();
+ if (listener != null) {
+ listener.onTouchStart();
+ }
+ }
+
+ @Override
+ public void onUpOrCancel() {
+ final WindowOrientationListener listener = getOrientationListener();
+ if (listener != null) {
+ listener.onTouchEnd();
+ }
+ }
+
+ @Override
+ public void onMouseHoverAtTop() {
+ mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
+ Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
+ msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS;
+ mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
+ }
+
+ @Override
+ public void onMouseHoverAtBottom() {
+ mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
+ Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
+ msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION;
+ mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
+ }
+
+ @Override
+ public void onMouseLeaveFromEdge() {
+ mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
+ }
+ });
+ displayContent.registerPointerEventListener(mSystemGestures);
+ displayContent.mAppTransition.registerListenerLocked(
+ mStatusBarController.getAppTransitionListener());
+ mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
+ mService.mVrModeEnabled);
+ mAcquireSleepTokenRunnable = () -> {
+ if (mWindowSleepToken != null) {
+ return;
+ }
+ final int displayId = displayContent.getDisplayId();
+ mWindowSleepToken = service.mAtmInternal.acquireSleepToken(
+ "WindowSleepTokenOnDisplay" + displayId, displayId);
+ };
+ mReleaseSleepTokenRunnable = () -> {
+ if (mWindowSleepToken == null) {
+ return;
+ }
+ mWindowSleepToken.release();
+ mWindowSleepToken = null;
+ };
+
+ // TODO: Make it can take screenshot on external display
+ mScreenshotHelper = displayContent.isDefaultDisplay
+ ? new ScreenshotHelper(mContext) : null;
+ }
+
+ void systemReady() {
+ mSystemGestures.systemReady();
+ }
+
+ private int getDisplayId() {
+ return mDisplayContent.getDisplayId();
+ }
+
+ void onDisplayRemoved() {
+ mDisplayContent.unregisterPointerEventListener(mSystemGestures);
}
void configure(int width, int height, int shortSizeDp) {
// Allow the navigation bar to move on non-square small devices (phones).
mNavigationBarCanMove = width != height && shortSizeDp < 600;
- mHasNavigationBar = mService.mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_showNavigationBar);
+ if (mDisplayContent.isDefaultDisplay) {
+ mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar);
- // Allow a system property to override this. Used by the emulator.
- // See also hasNavigationBar().
- String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
- if ("1".equals(navBarOverride)) {
- mHasNavigationBar = false;
- } else if ("0".equals(navBarOverride)) {
- mHasNavigationBar = true;
+ // Allow a system property to override this. Used by the emulator.
+ // See also hasNavigationBar().
+ String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
+ if ("1".equals(navBarOverride)) {
+ mHasNavigationBar = false;
+ } else if ("0".equals(navBarOverride)) {
+ mHasNavigationBar = true;
+ }
+ } else {
+ mHasNavigationBar = mDisplayContent.getDisplay().supportsSystemDecorations();
}
}
+ void updateConfigurationDependentBehaviors() {
+ mNavBarOpacityMode = mContext.getResources().getInteger(R.integer.config_navBarOpacityMode);
+ }
+
public void setHdmiPlugged(boolean plugged) {
setHdmiPlugged(plugged, false /* force */);
}
@@ -101,7 +554,7 @@
final Intent intent = new Intent(ACTION_HDMI_PLUGGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
- mService.mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
}
@@ -244,17 +697,2656 @@
return true;
}
+ /**
+ * Sanitize the layout parameters coming from a client. Allows the policy
+ * to do things like ensure that windows of a specific type can't take
+ * input focus.
+ *
+ * @param attrs The window layout parameters to be modified. These values
+ * are modified in-place.
+ */
+ public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
+ boolean hasStatusBarServicePermission) {
+
+ final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
+ if (mScreenDecorWindows.contains(win)) {
+ if (!isScreenDecor) {
+ // No longer has the flag set, so remove from the set.
+ mScreenDecorWindows.remove(win);
+ }
+ } else if (isScreenDecor && hasStatusBarServicePermission) {
+ mScreenDecorWindows.add(win);
+ }
+
+ switch (attrs.type) {
+ case TYPE_SYSTEM_OVERLAY:
+ case TYPE_SECURE_SYSTEM_OVERLAY:
+ // These types of windows can't receive input events.
+ attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+ break;
+ case TYPE_DREAM:
+ case TYPE_WALLPAPER:
+ // Dreams and wallpapers don't have an app window token and can thus not be
+ // letterboxed. Hence always let them extend under the cutout.
+ attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ break;
+ case TYPE_STATUS_BAR:
+
+ // If the Keyguard is in a hidden state (occluded by another window), we force to
+ // remove the wallpaper and keyguard flag so that any change in-flight after setting
+ // the keyguard as occluded wouldn't set these flags again.
+ // See {@link #processKeyguardSetHiddenResultLw}.
+ if (mService.mPolicy.isKeyguardOccluded()) {
+ attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+ attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+ }
+ break;
+
+ case TYPE_SCREENSHOT:
+ attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ break;
+
+ case TYPE_TOAST:
+ // While apps should use the dedicated toast APIs to add such windows
+ // it possible legacy apps to add the window directly. Therefore, we
+ // make windows added directly by the app behave as a toast as much
+ // as possible in terms of timeout and animation.
+ if (attrs.hideTimeoutMilliseconds < 0
+ || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) {
+ attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
+ }
+ attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
+ break;
+ }
+
+ if (attrs.type != TYPE_STATUS_BAR) {
+ // The status bar is the only window allowed to exhibit keyguard behavior.
+ attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+ }
+ }
+
+ /**
+ * Preflight adding a window to the system.
+ *
+ * Currently enforces that three window types are singletons per display:
+ * <ul>
+ * <li>{@link WindowManager.LayoutParams#TYPE_STATUS_BAR}</li>
+ * <li>{@link WindowManager.LayoutParams#TYPE_NAVIGATION_BAR}</li>
+ * </ul>
+ *
+ * @param win The window to be added
+ * @param attrs Information about the window to be added
+ *
+ * @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons,
+ * WindowManagerImpl.ADD_MULTIPLE_SINGLETON
+ */
+ public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) {
+
+ if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE,
+ "DisplayPolicy");
+ mScreenDecorWindows.add(win);
+ }
+
+ switch (attrs.type) {
+ case TYPE_STATUS_BAR:
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE,
+ "DisplayPolicy");
+ if (mStatusBar != null) {
+ if (mStatusBar.isAlive()) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ }
+ mStatusBar = win;
+ mStatusBarController.setWindow(win);
+ 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(
+ android.Manifest.permission.STATUS_BAR_SERVICE,
+ "DisplayPolicy");
+ if (mNavigationBar != null) {
+ if (mNavigationBar.isAlive()) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ }
+ mNavigationBar = win;
+ 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:
+ case TYPE_STATUS_BAR_PANEL:
+ case TYPE_STATUS_BAR_SUB_PANEL:
+ case TYPE_VOICE_INTERACTION_STARTING:
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE,
+ "DisplayPolicy");
+ break;
+ }
+ return ADD_OKAY;
+ }
+
+ /**
+ * Called when a window is being removed from a window manager. Must not
+ * throw an exception -- clean up as much as possible.
+ *
+ * @param win The window being removed.
+ */
+ public void removeWindowLw(WindowState win) {
+ if (mStatusBar == win) {
+ mStatusBar = null;
+ mStatusBarController.setWindow(null);
+ 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;
+ }
+ 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
+ * to use the default animation.
+ *
+ * @param win The window that is changing.
+ * @param transit What is happening to the window:
+ * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_ENTER},
+ * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_EXIT},
+ * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_SHOW}, or
+ * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_HIDE}.
+ *
+ * @return Resource ID of the actual animation to use, or 0 for none.
+ */
+ public int selectAnimationLw(WindowState win, int transit) {
+ if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win
+ + ": transit=" + transit);
+ if (win == mStatusBar) {
+ final boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
+ final boolean expanded = win.getAttrs().height == MATCH_PARENT
+ && win.getAttrs().width == MATCH_PARENT;
+ if (isKeyguard || expanded) {
+ return -1;
+ }
+ if (transit == TRANSIT_EXIT
+ || transit == TRANSIT_HIDE) {
+ return R.anim.dock_top_exit;
+ } else if (transit == TRANSIT_ENTER
+ || transit == TRANSIT_SHOW) {
+ return R.anim.dock_top_enter;
+ }
+ } else if (win == mNavigationBar) {
+ if (win.getAttrs().windowAnimations != 0) {
+ return 0;
+ }
+ // This can be on either the bottom or the right or the left.
+ if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ if (transit == TRANSIT_EXIT
+ || transit == TRANSIT_HIDE) {
+ if (mService.mPolicy.isKeyguardShowingAndNotOccluded()) {
+ return R.anim.dock_bottom_exit_keyguard;
+ } else {
+ return R.anim.dock_bottom_exit;
+ }
+ } else if (transit == TRANSIT_ENTER
+ || transit == TRANSIT_SHOW) {
+ return R.anim.dock_bottom_enter;
+ }
+ } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+ if (transit == TRANSIT_EXIT
+ || transit == TRANSIT_HIDE) {
+ return R.anim.dock_right_exit;
+ } else if (transit == TRANSIT_ENTER
+ || transit == TRANSIT_SHOW) {
+ return R.anim.dock_right_enter;
+ }
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ if (transit == TRANSIT_EXIT
+ || transit == TRANSIT_HIDE) {
+ return R.anim.dock_left_exit;
+ } else if (transit == TRANSIT_ENTER
+ || transit == TRANSIT_SHOW) {
+ return R.anim.dock_left_enter;
+ }
+ }
+ } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
+ return selectDockedDividerAnimationLw(win, transit);
+ }
+
+ if (transit == TRANSIT_PREVIEW_DONE) {
+ if (win.hasAppShownWindows()) {
+ if (DEBUG_ANIM) Slog.i(TAG, "**** STARTING EXIT");
+ return R.anim.app_starting_exit;
+ }
+ } else if (win.getAttrs().type == TYPE_DREAM && mDreamingLockscreen
+ && transit == TRANSIT_ENTER) {
+ // Special case: we are animating in a dream, while the keyguard
+ // is shown. We don't want an animation on the dream, because
+ // we need it shown immediately with the keyguard animating away
+ // to reveal it.
+ return -1;
+ }
+
+ return 0;
+ }
+
+ private int selectDockedDividerAnimationLw(WindowState win, int transit) {
+ int insets = mDisplayContent.getDockedDividerController().getContentInsets();
+
+ // If the divider is behind the navigation bar, don't animate.
+ final Rect frame = win.getFrameLw();
+ final boolean behindNavBar = mNavigationBar != null
+ && ((mNavigationBarPosition == NAV_BAR_BOTTOM
+ && frame.top + insets >= mNavigationBar.getFrameLw().top)
+ || (mNavigationBarPosition == NAV_BAR_RIGHT
+ && frame.left + insets >= mNavigationBar.getFrameLw().left)
+ || (mNavigationBarPosition == NAV_BAR_LEFT
+ && frame.right - insets <= mNavigationBar.getFrameLw().right));
+ final boolean landscape = frame.height() > frame.width();
+ final boolean offscreenLandscape = landscape && (frame.right - insets <= 0
+ || frame.left + insets >= win.getDisplayFrameLw().right);
+ final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0
+ || frame.bottom + insets >= win.getDisplayFrameLw().bottom);
+ final boolean offscreen = offscreenLandscape || offscreenPortrait;
+ if (behindNavBar || offscreen) {
+ return 0;
+ }
+ if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
+ return R.anim.fade_in;
+ } else if (transit == TRANSIT_EXIT) {
+ return R.anim.fade_out;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Determine the animation to run for a rotation transition based on the
+ * top fullscreen windows {@link WindowManager.LayoutParams#rotationAnimation}
+ * and whether it is currently fullscreen and frontmost.
+ *
+ * @param anim The exiting animation resource id is stored in anim[0], the
+ * entering animation resource id is stored in anim[1].
+ */
+ public void selectRotationAnimationLw(int anim[]) {
+ // If the screen is off or non-interactive, force a jumpcut.
+ final boolean forceJumpcut = !mScreenOnFully || !mService.mPolicy.okToAnimate();
+ if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen="
+ + mTopFullscreenOpaqueWindowState + " rotationAnimation="
+ + (mTopFullscreenOpaqueWindowState == null
+ ? "0" : mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation)
+ + " forceJumpcut=" + forceJumpcut);
+ if (forceJumpcut) {
+ anim[0] = R.anim.rotation_animation_jump_exit;
+ anim[1] = R.anim.rotation_animation_enter;
+ return;
+ }
+ if (mTopFullscreenOpaqueWindowState != null) {
+ int animationHint = mTopFullscreenOpaqueWindowState.getRotationAnimationHint();
+ if (animationHint < 0 && mTopIsFullscreen) {
+ animationHint = mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation;
+ }
+ switch (animationHint) {
+ case ROTATION_ANIMATION_CROSSFADE:
+ case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless.
+ anim[0] = R.anim.rotation_animation_xfade_exit;
+ anim[1] = R.anim.rotation_animation_enter;
+ break;
+ case ROTATION_ANIMATION_JUMPCUT:
+ anim[0] = R.anim.rotation_animation_jump_exit;
+ anim[1] = R.anim.rotation_animation_enter;
+ break;
+ case ROTATION_ANIMATION_ROTATE:
+ default:
+ anim[0] = anim[1] = 0;
+ break;
+ }
+ } else {
+ anim[0] = anim[1] = 0;
+ }
+ }
+
+ /**
+ * Validate whether the current top fullscreen has specified the same
+ * {@link WindowManager.LayoutParams#rotationAnimation} value as that
+ * being passed in from the previous top fullscreen window.
+ *
+ * @param exitAnimId exiting resource id from the previous window.
+ * @param enterAnimId entering resource id from the previous window.
+ * @param forceDefault For rotation animations only, if true ignore the
+ * animation values and just return false.
+ * @return true if the previous values are still valid, false if they
+ * should be replaced with the default.
+ */
+ public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId,
+ boolean forceDefault) {
+ switch (exitAnimId) {
+ case R.anim.rotation_animation_xfade_exit:
+ case R.anim.rotation_animation_jump_exit:
+ // These are the only cases that matter.
+ if (forceDefault) {
+ return false;
+ }
+ int anim[] = new int[2];
+ selectRotationAnimationLw(anim);
+ return (exitAnimId == anim[0] && enterAnimId == anim[1]);
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Called when a new system UI visibility is being reported, allowing
+ * the policy to adjust what is actually reported.
+ * @param visibility The raw visibility reported by the status bar.
+ * @return The new desired visibility.
+ */
+ public int adjustSystemUiVisibilityLw(int visibility) {
+ mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
+ mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
+
+ // Reset any bits in mForceClearingStatusBarVisibility that
+ // are now clear.
+ mResettingSystemUiFlags &= visibility;
+ // Clear any bits in the new visibility that are currently being
+ // force cleared, before reporting it.
+ return visibility & ~mResettingSystemUiFlags
+ & ~mForceClearedSystemUiFlags;
+ }
+
+ /**
+ * @return true if the navigation bar is forced to stay visible
+ */
+ public boolean isNavBarForcedShownLw(WindowState windowState) {
+ return mForceShowSystemBars;
+ }
+
+ // TODO: Should probably be moved into DisplayFrames.
+ /**
+ * Return the layout hints for a newly added window. These values are computed on the
+ * most recent layout, so they are not guaranteed to be correct.
+ *
+ * @param attrs The LayoutParams of the window.
+ * @param taskBounds The bounds of the task this window is on or {@code null} if no task is
+ * associated with the window.
+ * @param displayFrames display frames.
+ * @param floatingStack Whether the window's stack is floating.
+ * @param outFrame The frame of the window.
+ * @param outContentInsets The areas covered by system windows, expressed as positive insets.
+ * @param outStableInsets The areas covered by stable system windows irrespective of their
+ * current visibility. Expressed as positive insets.
+ * @param outOutsets The areas that are not real display, but we would like to treat as such.
+ * @param outDisplayCutout The area that has been cut away from the display.
+ * @return Whether to always consume the navigation bar.
+ * See {@link #isNavBarForcedShownLw(WindowState)}.
+ */
+ public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds,
+ DisplayFrames displayFrames, boolean floatingStack, Rect outFrame,
+ Rect outContentInsets, Rect outStableInsets,
+ Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
+ final int fl = PolicyControl.getWindowFlags(null, attrs);
+ final int pfl = attrs.privateFlags;
+ final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
+ final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs);
+ final int displayRotation = displayFrames.mRotation;
+
+ final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
+ if (useOutsets) {
+ int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
+ if (outset > 0) {
+ if (displayRotation == Surface.ROTATION_0) {
+ outOutsets.bottom += outset;
+ } else if (displayRotation == Surface.ROTATION_90) {
+ outOutsets.right += outset;
+ } else if (displayRotation == Surface.ROTATION_180) {
+ outOutsets.top += outset;
+ } else if (displayRotation == Surface.ROTATION_270) {
+ outOutsets.left += outset;
+ }
+ }
+ }
+
+ final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0;
+ final boolean layoutInScreenAndInsetDecor = layoutInScreen
+ && (fl & FLAG_LAYOUT_INSET_DECOR) != 0;
+ final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
+
+ if (layoutInScreenAndInsetDecor && !screenDecor) {
+ if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
+ outFrame.set(displayFrames.mUnrestricted);
+ } else {
+ outFrame.set(displayFrames.mRestricted);
+ }
+
+ final Rect sf;
+ if (floatingStack) {
+ sf = null;
+ } else {
+ sf = displayFrames.mStable;
+ }
+
+ final Rect cf;
+ if (floatingStack) {
+ cf = null;
+ } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+ if ((fl & FLAG_FULLSCREEN) != 0) {
+ cf = displayFrames.mStableFullscreen;
+ } else {
+ cf = displayFrames.mStable;
+ }
+ } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
+ cf = displayFrames.mOverscan;
+ } else {
+ cf = displayFrames.mCurrent;
+ }
+
+ if (taskBounds != null) {
+ outFrame.intersect(taskBounds);
+ }
+ InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets);
+ InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets);
+ outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame)
+ .getDisplayCutout());
+ return mForceShowSystemBars;
+ } else {
+ if (layoutInScreen) {
+ outFrame.set(displayFrames.mUnrestricted);
+ } else {
+ outFrame.set(displayFrames.mStable);
+ }
+ if (taskBounds != null) {
+ outFrame.intersect(taskBounds);
+ }
+
+ outContentInsets.setEmpty();
+ outStableInsets.setEmpty();
+ outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
+ return mForceShowSystemBars;
+ }
+ }
+
+ private static int getImpliedSysUiFlagsForLayout(LayoutParams attrs) {
+ int impliedFlags = 0;
+ if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
+ impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ }
+ final boolean forceWindowDrawsStatusBarBackground =
+ (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
+ if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
+ || forceWindowDrawsStatusBarBackground
+ && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) {
+ impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ }
+ return impliedFlags;
+ }
+
+ private static boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) {
+ return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0;
+ }
+
+ private final Runnable mClearHideNavigationFlag = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ mForceClearedSystemUiFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ mDisplayContent.reevaluateStatusBarVisibility();
+ }
+ }
+ };
+
+ /**
+ * Input handler used while nav bar is hidden. Captures any touch on the screen,
+ * to determine when the nav bar should be shown and prevent applications from
+ * receiving those touches.
+ */
+ private final class HideNavInputEventReceiver extends InputEventReceiver {
+ HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ try {
+ if (event instanceof MotionEvent
+ && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ final MotionEvent motionEvent = (MotionEvent) event;
+ if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
+ // When the user taps down, we re-show the nav bar.
+ boolean changed = false;
+ synchronized (mLock) {
+ if (mInputConsumer == null) {
+ return;
+ }
+ // Any user activity always causes us to show the
+ // navigation controls, if they had been hidden.
+ // We also clear the low profile and only content
+ // flags so that tapping on the screen will atomically
+ // restore all currently hidden screen decorations.
+ int newVal = mResettingSystemUiFlags
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LOW_PROFILE
+ | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ if (mResettingSystemUiFlags != newVal) {
+ mResettingSystemUiFlags = newVal;
+ changed = true;
+ }
+ // We don't allow the system's nav bar to be hidden
+ // again for 1 second, to prevent applications from
+ // spamming us and keeping it from being shown.
+ newVal = mForceClearedSystemUiFlags
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ if (mForceClearedSystemUiFlags != newVal) {
+ mForceClearedSystemUiFlags = newVal;
+ changed = true;
+ mHandler.postDelayed(mClearHideNavigationFlag, 1000);
+ }
+ if (changed) {
+ mDisplayContent.reevaluateStatusBarVisibility();
+ }
+ }
+ }
+ }
+ } finally {
+ finishInputEvent(event, false /* handled */);
+ }
+ }
+ }
+
+ /**
+ * Called when layout of the windows is about to start.
+ *
+ * @param displayFrames frames of the display we are doing layout on.
+ * @param uiMode The current uiMode in configuration.
+ */
+ public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
+ displayFrames.onBeginLayout();
+ mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
+ mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
+
+ // For purposes of putting out fake window up to steal focus, we will
+ // drive nav being hidden only by whether it is requested.
+ final int sysui = mLastSystemUiFlags;
+ boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+ boolean navTranslucent = (sysui
+ & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
+ boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
+ boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
+ boolean navAllowedHidden = immersive || immersiveSticky;
+ navTranslucent &= !immersiveSticky; // transient trumps translucent
+ boolean isKeyguardShowing = isStatusBarKeyguard()
+ && !mService.mPolicy.isKeyguardOccluded();
+ if (!isKeyguardShowing) {
+ navTranslucent &= areTranslucentBarsAllowed();
+ }
+ boolean statusBarForcesShowingNavigation = !isKeyguardShowing && mStatusBar != null
+ && (mStatusBar.getAttrs().privateFlags
+ & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
+
+ // When the navigation bar isn't visible, we put up a fake input window to catch all
+ // touch events. This way we can detect when the user presses anywhere to bring back the
+ // nav bar and ensure the application doesn't see the event.
+ if (navVisible || navAllowedHidden) {
+ if (mInputConsumer != null) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
+ mInputConsumer = null;
+ }
+ } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
+ mInputConsumer = mService.createInputConsumer(mHandler.getLooper(),
+ INPUT_CONSUMER_NAVIGATION,
+ HideNavInputEventReceiver::new,
+ displayFrames.mDisplayId);
+ // As long as mInputConsumer is active, hover events are not dispatched to the app
+ // and the pointer icon is likely to become stale. Hide it to avoid confusion.
+ InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
+ }
+
+ // For purposes of positioning and showing the nav bar, if we have decided that it can't
+ // be hidden (because of the screen aspect ratio), then take that into account.
+ navVisible |= !canHideNavigationBar();
+
+ boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
+ navTranslucent, navAllowedHidden, statusBarForcesShowingNavigation);
+ if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
+ updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing);
+ if (updateSysUiVisibility) {
+ updateSystemUiVisibilityLw();
+ }
+ layoutScreenDecorWindows(displayFrames);
+
+ if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
+ // Make sure that the zone we're avoiding for the cutout is at least as tall as the
+ // status bar; otherwise fullscreen apps will end up cutting halfway into the status
+ // bar.
+ displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,
+ displayFrames.mStable.top);
+ }
+ }
+
+ private void layoutScreenDecorWindows(DisplayFrames displayFrames) {
+ if (mScreenDecorWindows.isEmpty()) {
+ return;
+ }
+
+ sTmpRect.setEmpty();
+ sTmpDockedFrame.set(displayFrames.mDock);
+
+ final int displayId = displayFrames.mDisplayId;
+ final Rect dockFrame = displayFrames.mDock;
+ final int displayHeight = displayFrames.mDisplayHeight;
+ final int displayWidth = displayFrames.mDisplayWidth;
+
+ for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) {
+ final WindowState w = mScreenDecorWindows.valueAt(i);
+ if (w.getDisplayId() != displayId || !w.isVisibleLw()) {
+ // Skip if not on the same display or not visible.
+ continue;
+ }
+
+ w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */,
+ sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */,
+ sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */,
+ sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */,
+ sTmpDockedFrame /* outsetFrame */);
+ w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+ w.computeFrameLw();
+ final Rect frame = w.getFrameLw();
+
+ if (frame.left <= 0 && frame.top <= 0) {
+ // Docked at left or top.
+ if (frame.bottom >= displayHeight) {
+ // Docked left.
+ dockFrame.left = Math.max(frame.right, dockFrame.left);
+ } else if (frame.right >= displayWidth) {
+ // Docked top.
+ dockFrame.top = Math.max(frame.bottom, dockFrame.top);
+ } else {
+ Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
+ + " not docked on left or top of display. frame=" + frame
+ + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
+ }
+ } else if (frame.right >= displayWidth && frame.bottom >= displayHeight) {
+ // Docked at right or bottom.
+ if (frame.top <= 0) {
+ // Docked right.
+ dockFrame.right = Math.min(frame.left, dockFrame.right);
+ } else if (frame.left <= 0) {
+ // Docked bottom.
+ dockFrame.bottom = Math.min(frame.top, dockFrame.bottom);
+ } else {
+ Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
+ + " not docked on right or bottom" + " of display. frame=" + frame
+ + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
+ }
+ } else {
+ // Screen decor windows are required to be docked on one of the sides of the screen.
+ Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
+ + " not docked on one of the sides of the display. frame=" + frame
+ + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
+ }
+ }
+
+ displayFrames.mRestricted.set(dockFrame);
+ displayFrames.mCurrent.set(dockFrame);
+ displayFrames.mVoiceContent.set(dockFrame);
+ displayFrames.mSystem.set(dockFrame);
+ displayFrames.mContent.set(dockFrame);
+ displayFrames.mRestrictedOverscan.set(dockFrame);
+ }
+
+ private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
+ boolean isKeyguardShowing) {
+ // decide where the status bar goes ahead of time
+ if (mStatusBar == null) {
+ return false;
+ }
+ // apply any navigation bar insets
+ sTmpRect.setEmpty();
+ mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
+ displayFrames.mUnrestricted /* displayFrame */,
+ displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
+ displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
+ displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
+ mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+
+ // Let the status bar determine its size.
+ mStatusBar.computeFrameLw();
+
+ // For layout, the status bar is always at the top with our fixed height.
+ displayFrames.mStable.top = displayFrames.mUnrestricted.top
+ + mStatusBarHeightForRotation[displayFrames.mRotation];
+ // Make sure the status bar covers the entire cutout height
+ displayFrames.mStable.top = Math.max(displayFrames.mStable.top,
+ displayFrames.mDisplayCutoutSafe.top);
+
+ // Tell the bar controller where the collapsed status bar content is
+ sTmpRect.set(mStatusBar.getContentFrameLw());
+ sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+ sTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset
+ sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
+ mStatusBarController.setContentFrame(sTmpRect);
+
+ boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
+ boolean statusBarTranslucent = (sysui
+ & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
+ if (!isKeyguardShowing) {
+ statusBarTranslucent &= areTranslucentBarsAllowed();
+ }
+
+ // If the status bar is hidden, we don't want to cause windows behind it to scroll.
+ if (mStatusBar.isVisibleLw() && !statusBarTransient) {
+ // Status bar may go away, so the screen area it occupies is available to apps but just
+ // covering them when the status bar is visible.
+ final Rect dockFrame = displayFrames.mDock;
+ dockFrame.top = displayFrames.mStable.top;
+ displayFrames.mContent.set(dockFrame);
+ displayFrames.mVoiceContent.set(dockFrame);
+ displayFrames.mCurrent.set(dockFrame);
+
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
+ "dock=%s content=%s cur=%s", dockFrame.toString(),
+ displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
+
+ if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
+ && !mStatusBarController.wasRecentlyTranslucent()) {
+ // If the opaque status bar is currently requested to be visible, and not in the
+ // process of animating on or off, then we can tell the app that it is covered by
+ // it.
+ displayFrames.mSystem.top = displayFrames.mStable.top;
+ }
+ }
+ return mStatusBarController.checkHiddenLw();
+ }
+
+ private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
+ boolean navTranslucent, boolean navAllowedHidden,
+ boolean statusBarForcesShowingNavigation) {
+ if (mNavigationBar == null) {
+ return false;
+ }
+
+ final Rect navigationFrame = sTmpNavFrame;
+ boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
+ // Force the navigation bar to its appropriate place and size. We need to do this directly,
+ // instead of relying on it to bubble up from the nav bar, because this needs to change
+ // atomically with screen rotations.
+ final int rotation = displayFrames.mRotation;
+ final int displayHeight = displayFrames.mDisplayHeight;
+ final int displayWidth = displayFrames.mDisplayWidth;
+ final Rect dockFrame = displayFrames.mDock;
+ mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
+
+ final Rect cutoutSafeUnrestricted = sTmpRect;
+ cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
+ cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
+
+ if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ // It's a system nav bar or a portrait screen; nav bar goes on bottom.
+ final int top = cutoutSafeUnrestricted.bottom
+ - getNavigationBarHeight(rotation, uiMode);
+ // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+ final int topNavBar = cutoutSafeUnrestricted.bottom
+ - mExperiments.getNavigationBarFrameHeight();
+ navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom);
+ // EXPERIMENT END
+ displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ dockFrame.bottom = displayFrames.mRestricted.bottom =
+ displayFrames.mRestrictedOverscan.bottom = top;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status bar.
+ mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the opaque nav bar is currently requested to be visible and not in the process
+ // of animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.bottom = top;
+ }
+ } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+ // Landscape screen; nav bar goes to the right.
+ final int left = cutoutSafeUnrestricted.right
+ - getNavigationBarWidth(rotation, uiMode);
+ // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+ final int leftNavBar = cutoutSafeUnrestricted.right
+ - mExperiments.getNavigationBarFrameWidth();
+ navigationFrame.set(leftNavBar, 0, displayFrames.mUnrestricted.right, displayHeight);
+ // EXPERIMENT END
+ displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ dockFrame.right = displayFrames.mRestricted.right =
+ displayFrames.mRestrictedOverscan.right = left;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status bar.
+ mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible, and not in the process of
+ // animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.right = left;
+ }
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ // Seascape screen; nav bar goes to the left.
+ final int right = cutoutSafeUnrestricted.left
+ + getNavigationBarWidth(rotation, uiMode);
+ // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+ final int rightNavBar = cutoutSafeUnrestricted.left
+ + mExperiments.getNavigationBarFrameWidth();
+ navigationFrame.set(displayFrames.mUnrestricted.left, 0, rightNavBar, displayHeight);
+ // EXPERIMENT END
+ displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ dockFrame.left = displayFrames.mRestricted.left =
+ displayFrames.mRestrictedOverscan.left = right;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status bar.
+ mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible, and not in the process of
+ // animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.left = right;
+ }
+ }
+
+ // Make sure the content and current rectangles are updated to account for the restrictions
+ // from the navigation bar.
+ displayFrames.mCurrent.set(dockFrame);
+ displayFrames.mVoiceContent.set(dockFrame);
+ displayFrames.mContent.set(dockFrame);
+ // And compute the final frame.
+ sTmpRect.setEmpty();
+ mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */,
+ navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
+ displayFrames.mDisplayCutoutSafe /* contentFrame */,
+ navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
+ navigationFrame /* stableFrame */,
+ displayFrames.mDisplayCutoutSafe /* outsetFrame */);
+ mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+ mNavigationBar.computeFrameLw();
+ mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
+
+ if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
+ return mNavigationBarController.checkHiddenLw();
+ }
+
+ private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
+ boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf,
+ DisplayFrames displayFrames) {
+ if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) {
+ // Here's a special case: if the child window is not the 'dock window'
+ // or input method target, and the window it is attached to is below
+ // the dock window, then the frames we computed for the window it is
+ // attached to can not be used because the dock is effectively part
+ // of the underlying window and the attached window is floating on top
+ // of the whole thing. So, we ignore the attached window and explicitly
+ // compute the frames that would be appropriate without the dock.
+ vf.set(displayFrames.mDock);
+ cf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
+ } else {
+ // The effective display frame of the attached window depends on whether it is taking
+ // care of insetting its content. If not, we need to use the parent's content frame so
+ // that the entire window is positioned within that content. Otherwise we can use the
+ // overscan frame and let the attached window take care of positioning its content
+ // appropriately.
+ if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+ // Set the content frame of the attached window to the parent's decor frame
+ // (same as content frame when IME isn't present) if specifically requested by
+ // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag.
+ // Otherwise, use the overscan frame.
+ cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0
+ ? attached.getContentFrameLw() : attached.getOverscanFrameLw());
+ } else {
+ // If the window is resizing, then we want to base the content frame on our attached
+ // content frame to resize...however, things can be tricky if the attached window is
+ // NOT in resize mode, in which case its content frame will be larger.
+ // Ungh. So to deal with that, make sure the content frame we end up using is not
+ // covering the IM dock.
+ cf.set(attached.getContentFrameLw());
+ if (attached.isVoiceInteraction()) {
+ cf.intersectUnchecked(displayFrames.mVoiceContent);
+ } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) {
+ cf.intersectUnchecked(displayFrames.mContent);
+ }
+ }
+ df.set(insetDecors ? attached.getDisplayFrameLw() : cf);
+ of.set(insetDecors ? attached.getOverscanFrameLw() : cf);
+ vf.set(attached.getVisibleFrameLw());
+ }
+ // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be
+ // positioned relative to its parent or the entire screen.
+ pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df);
+ }
+
+ private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) {
+ if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) {
+ return;
+ }
+ // If app is requesting a stable layout, don't let the content insets go below the stable
+ // values.
+ if ((fl & FLAG_FULLSCREEN) != 0) {
+ r.intersectUnchecked(displayFrames.mStableFullscreen);
+ } else {
+ r.intersectUnchecked(displayFrames.mStable);
+ }
+ }
+
+ private boolean canReceiveInput(WindowState win) {
+ boolean notFocusable =
+ (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0;
+ boolean altFocusableIm =
+ (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0;
+ boolean notFocusableForIm = notFocusable ^ altFocusableIm;
+ return !notFocusableForIm;
+ }
+
+ /**
+ * Called for each window attached to the window manager as layout is proceeding. The
+ * implementation of this function must take care of setting the window's frame, either here or
+ * in finishLayout().
+ *
+ * @param win The window being positioned.
+ * @param attached For sub-windows, the window it is attached to; this
+ * window will already have had layoutWindow() called on it
+ * so you can use its Rect. Otherwise null.
+ * @param displayFrames The display frames.
+ */
+ public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
+ // We've already done the navigation bar, status bar, and all screen decor windows. If the
+ // status bar can receive input, we need to layout it again to accommodate for the IME
+ // window.
+ if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar
+ || mScreenDecorWindows.contains(win)) {
+ return;
+ }
+ final WindowManager.LayoutParams attrs = win.getAttrs();
+ final boolean isDefaultDisplay = win.isDefaultDisplay();
+
+ final int type = attrs.type;
+ final int fl = PolicyControl.getWindowFlags(win, attrs);
+ final int pfl = attrs.privateFlags;
+ final int sim = attrs.softInputMode;
+ final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
+ final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
+
+ final WindowFrames windowFrames = win.getWindowFrames();
+
+ windowFrames.setHasOutsets(false);
+ sTmpLastParentFrame.set(windowFrames.mParentFrame);
+ final Rect pf = windowFrames.mParentFrame;
+ final Rect df = windowFrames.mDisplayFrame;
+ final Rect of = windowFrames.mOverscanFrame;
+ final Rect cf = windowFrames.mContentFrame;
+ final Rect vf = windowFrames.mVisibleFrame;
+ final Rect dcf = windowFrames.mDecorFrame;
+ final Rect sf = windowFrames.mStableFrame;
+ dcf.setEmpty();
+ windowFrames.setParentFrameWasClippedByDisplayCutout(false);
+ windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
+
+ final boolean hasNavBar = hasNavigationBar() && mNavigationBar != null
+ && mNavigationBar.isVisibleLw();
+
+ final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
+
+ final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
+ || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+
+ final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
+ final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
+
+ sf.set(displayFrames.mStable);
+
+ if (type == TYPE_INPUT_METHOD) {
+ vf.set(displayFrames.mDock);
+ cf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
+ windowFrames.mParentFrame.set(displayFrames.mDock);
+ // IM dock windows layout below the nav bar...
+ pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
+ // ...with content insets above the nav bar
+ cf.bottom = vf.bottom = displayFrames.mStable.bottom;
+ if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
+ // The status bar forces the navigation bar while it's visible. Make sure the IME
+ // avoids the navigation bar in that case.
+ if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+ pf.right = df.right = of.right = cf.right = vf.right =
+ displayFrames.mStable.right;
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
+ }
+ }
+
+ // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+ // Offset the ime to avoid overlapping with the nav bar
+ mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win);
+ // EXPERIMENT END
+
+ // IM dock windows always go to the bottom of the screen.
+ attrs.gravity = Gravity.BOTTOM;
+ } else if (type == TYPE_VOICE_INTERACTION) {
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+ cf.set(displayFrames.mDock);
+ } else {
+ cf.set(displayFrames.mContent);
+ }
+ if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ vf.set(displayFrames.mCurrent);
+ } else {
+ vf.set(cf);
+ }
+ } else if (type == TYPE_WALLPAPER) {
+ layoutWallpaper(displayFrames, pf, df, of, cf);
+ } else if (win == mStatusBar) {
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ cf.set(displayFrames.mStable);
+ vf.set(displayFrames.mStable);
+
+ if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
+ cf.bottom = displayFrames.mContent.bottom;
+ } else {
+ cf.bottom = displayFrames.mDock.bottom;
+ vf.bottom = displayFrames.mContent.bottom;
+ }
+ } else {
+ dcf.set(displayFrames.mSystem);
+ final boolean inheritTranslucentDecor =
+ (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
+ final boolean isAppWindow =
+ type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
+ final boolean topAtRest =
+ win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
+ if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
+ if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
+ && (fl & FLAG_FULLSCREEN) == 0
+ && (fl & FLAG_TRANSLUCENT_STATUS) == 0
+ && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+ && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) {
+ // Ensure policy decor includes status bar
+ dcf.top = displayFrames.mStable.top;
+ }
+ if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
+ && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
+ && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
+ // Ensure policy decor includes navigation bar
+ dcf.bottom = displayFrames.mStable.bottom;
+ dcf.right = displayFrames.mStable.right;
+ }
+ }
+
+ if (layoutInScreen && layoutInsetDecor) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ + "): IN_SCREEN, INSET_DECOR");
+ // This is the case for a normal activity window: we want it to cover all of the
+ // screen space, and it can take care of moving its contents to account for screen
+ // decorations that intrude into that space.
+ if (attached != null) {
+ // If this window is attached to another, our display
+ // frame is the same as the one we are attached to.
+ setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf,
+ displayFrames);
+ } else {
+ if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
+ // Status bar panels are the only windows who can go on top of the status
+ // bar. They are protected by the STATUS_BAR_SERVICE permission, so they
+ // have the same privileges as the status bar itself.
+ //
+ // However, they should still dodge the navigation bar if it exists.
+
+ pf.left = df.left = of.left = hasNavBar
+ ? displayFrames.mDock.left : displayFrames.mUnrestricted.left;
+ pf.top = df.top = of.top = displayFrames.mUnrestricted.top;
+ pf.right = df.right = of.right = hasNavBar
+ ? displayFrames.mRestricted.right
+ : displayFrames.mUnrestricted.right;
+ pf.bottom = df.bottom = of.bottom = hasNavBar
+ ? displayFrames.mRestricted.bottom
+ : displayFrames.mUnrestricted.bottom;
+
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf);
+ } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
+ && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+ // Asking to layout into the overscan region, so give it that pure
+ // unrestricted area.
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
+ && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW
+ || type == TYPE_VOLUME_OVERLAY)) {
+ // Asking for layout as if the nav bar is hidden, lets the application
+ // extend into the unrestricted overscan screen area. We only do this for
+ // application windows and certain system windows to ensure no window that
+ // can be above the nav bar can do this.
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ // We need to tell the app about where the frame inside the overscan is, so
+ // it can inset its content by that amount -- it didn't ask to actually
+ // extend itself into the overscan region.
+ of.set(displayFrames.mUnrestricted);
+ } else {
+ df.set(displayFrames.mRestrictedOverscan);
+ pf.set(displayFrames.mRestrictedOverscan);
+ // We need to tell the app about where the frame inside the overscan
+ // is, so it can inset its content by that amount -- it didn't ask
+ // to actually extend itself into the overscan region.
+ of.set(displayFrames.mUnrestricted);
+ }
+
+ if ((fl & FLAG_FULLSCREEN) == 0) {
+ if (win.isVoiceInteraction()) {
+ cf.set(displayFrames.mVoiceContent);
+ } else {
+ if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+ cf.set(displayFrames.mDock);
+ } else {
+ cf.set(displayFrames.mContent);
+ }
+ }
+ } else {
+ // Full screen windows are always given a layout that is as if the status
+ // bar and other transient decors are gone. This is to avoid bad states when
+ // moving from a window that is not hiding the status bar to one that is.
+ cf.set(displayFrames.mRestricted);
+ }
+ applyStableConstraints(sysUiFl, fl, cf, displayFrames);
+ if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ vf.set(displayFrames.mCurrent);
+ } else {
+ vf.set(cf);
+ }
+
+ // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+ mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win);
+ // EXPERIMENT END
+ }
+ } else if (layoutInScreen || (sysUiFl
+ & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ + "): IN_SCREEN");
+ // A window that has requested to fill the entire screen just
+ // gets everything, period.
+ if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ if (hasNavBar) {
+ pf.left = df.left = of.left = cf.left = displayFrames.mDock.left;
+ pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right;
+ pf.bottom = df.bottom = of.bottom = cf.bottom =
+ displayFrames.mRestricted.bottom;
+ }
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf);
+ } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
+ // The navigation bar has Real Ultimate Power.
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf);
+ } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
+ && ((fl & FLAG_FULLSCREEN) != 0)) {
+ // Fullscreen secure system overlays get what they ask for. Screenshot region
+ // selection overlay should also expand to full screen.
+ cf.set(displayFrames.mOverscan);
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ } else if (type == TYPE_BOOT_PROGRESS) {
+ // Boot progress screen always covers entire display.
+ cf.set(displayFrames.mOverscan);
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
+ && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+ // Asking to layout into the overscan region, so give it that pure unrestricted
+ // area.
+ cf.set(displayFrames.mOverscan);
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
+ && (type == TYPE_STATUS_BAR
+ || type == TYPE_TOAST
+ || type == TYPE_DOCK_DIVIDER
+ || type == TYPE_VOICE_INTERACTION_STARTING
+ || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) {
+ // Asking for layout as if the nav bar is hidden, lets the
+ // application extend into the unrestricted screen area. We
+ // only do this for application windows (or toasts) to ensure no window that
+ // can be above the nav bar can do this.
+ // XXX This assumes that an app asking for this will also
+ // ask for layout in only content. We can't currently figure out
+ // what the screen would be if only laying out to hide the nav bar.
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
+ of.set(displayFrames.mRestricted);
+ df.set(displayFrames.mRestricted);
+ pf.set(displayFrames.mRestricted);
+ if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+ cf.set(displayFrames.mDock);
+ } else {
+ cf.set(displayFrames.mContent);
+ }
+ } else {
+ cf.set(displayFrames.mRestricted);
+ of.set(displayFrames.mRestricted);
+ df.set(displayFrames.mRestricted);
+ pf.set(displayFrames.mRestricted);
+ }
+
+ applyStableConstraints(sysUiFl, fl, cf, displayFrames);
+
+ if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ vf.set(displayFrames.mCurrent);
+ } else {
+ vf.set(cf);
+ }
+ } else if (attached != null) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ + "): attached to " + attached);
+ // A child window should be placed inside of the same visible
+ // frame that its parent had.
+ setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf,
+ displayFrames);
+ } else {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ + "): normal window");
+ // Otherwise, a normal window must be placed inside the content
+ // of all screen decorations.
+ if (type == TYPE_STATUS_BAR_PANEL) {
+ // Status bar panels can go on
+ // top of the status bar. They are protected by the STATUS_BAR_SERVICE
+ // permission, so they have the same privileges as the status bar itself.
+ cf.set(displayFrames.mRestricted);
+ of.set(displayFrames.mRestricted);
+ df.set(displayFrames.mRestricted);
+ pf.set(displayFrames.mRestricted);
+ } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
+ // These dialogs are stable to interim decor changes.
+ cf.set(displayFrames.mStable);
+ of.set(displayFrames.mStable);
+ df.set(displayFrames.mStable);
+ pf.set(displayFrames.mStable);
+ } else {
+ pf.set(displayFrames.mContent);
+ if (win.isVoiceInteraction()) {
+ cf.set(displayFrames.mVoiceContent);
+ of.set(displayFrames.mVoiceContent);
+ df.set(displayFrames.mVoiceContent);
+ } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+ cf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
+ } else {
+ cf.set(displayFrames.mContent);
+ of.set(displayFrames.mContent);
+ df.set(displayFrames.mContent);
+ }
+ if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ vf.set(displayFrames.mCurrent);
+ } else {
+ vf.set(cf);
+ }
+ }
+ }
+ }
+
+ final int cutoutMode = attrs.layoutInDisplayCutoutMode;
+ final boolean attachedInParent = attached != null && !layoutInScreen;
+ final boolean requestedHideNavigation =
+ (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
+
+ // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get
+ // cropped / shifted to the displayFrame in WindowState.
+ final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
+ && type != TYPE_BASE_APPLICATION;
+
+ // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
+ // the cutout safe zone.
+ if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
+ final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
+ displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
+ if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
+ && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
+ // At the top we have the status bar, so apps that are
+ // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
+ // already expect that there's an inset there and we don't need to exclude
+ // the window from that area.
+ displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
+ }
+ if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation
+ && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
+ // Same for the navigation bar.
+ switch (mNavigationBarPosition) {
+ case NAV_BAR_BOTTOM:
+ displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+ break;
+ case NAV_BAR_RIGHT:
+ displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+ break;
+ case NAV_BAR_LEFT:
+ displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
+ break;
+ }
+ }
+ if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ // The IME can always extend under the bottom cutout if the navbar is there.
+ displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+ }
+ // Windows that are attached to a parent and laid out in said parent already avoid
+ // the cutout according to that parent and don't need to be further constrained.
+ // Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
+ // They will later be cropped or shifted using the displayFrame in WindowState,
+ // which prevents overlap with the DisplayCutout.
+ if (!attachedInParent && !floatingInScreenWindow) {
+ sTmpRect.set(pf);
+ pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+ windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
+ }
+ // Make sure that NO_LIMITS windows clipped to the display don't extend under the
+ // cutout.
+ df.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+ }
+
+ // Content should never appear in the cutout.
+ cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
+
+ // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
+ // Also, we don't allow windows in multi-window mode to extend out of the screen.
+ if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
+ && !win.isInMultiWindowMode()) {
+ df.left = df.top = -10000;
+ df.right = df.bottom = 10000;
+ if (type != TYPE_WALLPAPER) {
+ of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000;
+ of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
+ }
+ }
+
+ // If the device has a chin (e.g. some watches), a dead area at the bottom of the screen we
+ // need to provide information to the clients that want to pretend that you can draw there.
+ // We only want to apply outsets to certain types of windows. For example, we never want to
+ // apply the outsets to floating dialogs, because they wouldn't make sense there.
+ final boolean useOutsets = shouldUseOutsets(attrs, fl);
+ if (isDefaultDisplay && useOutsets) {
+ final Rect osf = windowFrames.mOutsetFrame;
+ osf.set(cf.left, cf.top, cf.right, cf.bottom);
+ windowFrames.setHasOutsets(true);
+ int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
+ if (outset > 0) {
+ int rotation = displayFrames.mRotation;
+ if (rotation == Surface.ROTATION_0) {
+ osf.bottom += outset;
+ } else if (rotation == Surface.ROTATION_90) {
+ osf.right += outset;
+ } else if (rotation == Surface.ROTATION_180) {
+ osf.top -= outset;
+ } else if (rotation == Surface.ROTATION_270) {
+ osf.left -= outset;
+ }
+ if (DEBUG_LAYOUT) Slog.v(TAG, "applying bottom outset of " + outset
+ + " with rotation " + rotation + ", result: " + osf);
+ }
+ }
+
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
+ + ": sim=#" + Integer.toHexString(sim)
+ + " attach=" + attached + " type=" + type
+ + String.format(" flags=0x%08x", fl)
+ + " pf=" + pf.toShortString() + " df=" + df.toShortString()
+ + " of=" + of.toShortString()
+ + " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
+ + " dcf=" + dcf.toShortString()
+ + " sf=" + sf.toShortString()
+ + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win);
+
+ if (!sTmpLastParentFrame.equals(pf)) {
+ windowFrames.setContentChanged(true);
+ }
+
+ win.computeFrameLw();
+ // Dock windows carve out the bottom of the screen, so normal windows
+ // can't appear underneath them.
+ if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
+ && !win.getGivenInsetsPendingLw()) {
+ offsetInputMethodWindowLw(win, displayFrames);
+ }
+ if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
+ && !win.getGivenInsetsPendingLw()) {
+ offsetVoiceInputWindowLw(win, displayFrames);
+ }
+ }
+
+ private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) {
+ // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area.
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
+ }
+
+ private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
+ int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
+ top += win.getGivenContentInsetsLw().top;
+ displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
+ displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
+ top = win.getVisibleFrameLw().top;
+ top += win.getGivenVisibleInsetsLw().top;
+ displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top);
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom="
+ + displayFrames.mDock.bottom + " mContentBottom="
+ + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom);
+ }
+
+ private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) {
+ int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
+ top += win.getGivenContentInsetsLw().top;
+ displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
+ }
+
+ /**
+ * Called following layout of all windows before each window has policy applied.
+ */
+ public void beginPostLayoutPolicyLw() {
+ mTopFullscreenOpaqueWindowState = null;
+ mTopFullscreenOpaqueOrDimmingWindowState = null;
+ mTopDockedOpaqueWindowState = null;
+ mTopDockedOpaqueOrDimmingWindowState = null;
+ mForceStatusBar = false;
+ mForceStatusBarFromKeyguard = false;
+ mForceStatusBarTransparent = false;
+ mForcingShowNavBar = false;
+ mForcingShowNavBarLayer = -1;
+
+ mAllowLockscreenWhenOn = false;
+ mShowingDream = false;
+ mWindowSleepTokenNeeded = false;
+ }
+
+ /**
+ * Called following layout of all window to apply policy to each window.
+ *
+ * @param win The window being positioned.
+ * @param attrs The LayoutParams of the window.
+ * @param attached For sub-windows, the window it is attached to. Otherwise null.
+ */
+ public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
+ WindowState attached, WindowState imeTarget) {
+ final boolean affectsSystemUi = win.canAffectSystemUiFlags();
+ if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
+ mService.mPolicy.applyKeyguardPolicyLw(win, imeTarget);
+ final int fl = PolicyControl.getWindowFlags(win, attrs);
+ if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi
+ && attrs.type == TYPE_INPUT_METHOD) {
+ mForcingShowNavBar = true;
+ mForcingShowNavBarLayer = win.getSurfaceLayer();
+ }
+ if (attrs.type == TYPE_STATUS_BAR) {
+ if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+ mForceStatusBarFromKeyguard = true;
+ }
+ if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
+ mForceStatusBarTransparent = true;
+ }
+ }
+
+ boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
+ && attrs.type < FIRST_SYSTEM_WINDOW;
+ final int windowingMode = win.getWindowingMode();
+ final boolean inFullScreenOrSplitScreenSecondaryWindowingMode =
+ windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) {
+ if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
+ mForceStatusBar = true;
+ }
+ if (attrs.type == TYPE_DREAM) {
+ // If the lockscreen was showing when the dream started then wait
+ // for the dream to draw before hiding the lockscreen.
+ if (!mDreamingLockscreen
+ || (win.isVisibleLw() && win.hasDrawnLw())) {
+ mShowingDream = true;
+ appWindow = true;
+ }
+ }
+
+ // For app windows that are not attached, we decide if all windows in the app they
+ // represent should be hidden or if we should hide the lockscreen. For attached app
+ // windows we defer the decision to the window it is attached to.
+ if (appWindow && attached == null) {
+ if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
+ mTopFullscreenOpaqueWindowState = win;
+ if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
+ mTopFullscreenOpaqueOrDimmingWindowState = win;
+ }
+ if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
+ mAllowLockscreenWhenOn = true;
+ }
+ }
+ }
+ }
+
+ // Voice interaction overrides both top fullscreen and top docked.
+ if (affectsSystemUi && win.getAttrs().type == TYPE_VOICE_INTERACTION) {
+ if (mTopFullscreenOpaqueWindowState == null) {
+ mTopFullscreenOpaqueWindowState = win;
+ if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
+ mTopFullscreenOpaqueOrDimmingWindowState = win;
+ }
+ }
+ if (mTopDockedOpaqueWindowState == null) {
+ mTopDockedOpaqueWindowState = win;
+ if (mTopDockedOpaqueOrDimmingWindowState == null) {
+ mTopDockedOpaqueOrDimmingWindowState = win;
+ }
+ }
+ }
+
+ // Keep track of the window if it's dimming but not necessarily fullscreen.
+ if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi
+ && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
+ mTopFullscreenOpaqueOrDimmingWindowState = win;
+ }
+
+ // We need to keep track of the top "fullscreen" opaque window for the docked stack
+ // separately, because both the "real fullscreen" opaque window and the one for the docked
+ // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+ if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
+ && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ mTopDockedOpaqueWindowState = win;
+ if (mTopDockedOpaqueOrDimmingWindowState == null) {
+ mTopDockedOpaqueOrDimmingWindowState = win;
+ }
+ }
+
+ // Also keep track of any windows that are dimming but not necessarily fullscreen in the
+ // docked stack.
+ if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming()
+ && 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;
+ }
+ }
+
+ /**
+ * Called following layout of all windows and after policy has been applied
+ * to each window. If in this function you do
+ * something that may have modified the animation state of another window,
+ * be sure to return non-zero in order to perform another pass through layout.
+ *
+ * @return Return any bit set of
+ * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_LAYOUT},
+ * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_CONFIG},
+ * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER}, or
+ * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
+ */
+ public int finishPostLayoutPolicyLw() {
+ int changes = 0;
+ boolean topIsFullscreen = false;
+
+ // If we are not currently showing a dream then remember the current
+ // lockscreen state. We will use this to determine whether the dream
+ // started while the lockscreen was showing and remember this state
+ // while the dream is showing.
+ if (!mShowingDream) {
+ mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded();
+ if (mDreamingSleepTokenNeeded) {
+ mDreamingSleepTokenNeeded = false;
+ mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget();
+ }
+ } else {
+ if (!mDreamingSleepTokenNeeded) {
+ mDreamingSleepTokenNeeded = true;
+ mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget();
+ }
+ }
+
+ if (mStatusBar != null) {
+ if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar
+ + " forcefkg=" + mForceStatusBarFromKeyguard
+ + " top=" + mTopFullscreenOpaqueWindowState);
+ boolean shouldBeTransparent = mForceStatusBarTransparent
+ && !mForceStatusBar
+ && !mForceStatusBarFromKeyguard;
+ if (!shouldBeTransparent) {
+ mStatusBarController.setShowTransparent(false /* transparent */);
+ } else if (!mStatusBar.isVisibleLw()) {
+ mStatusBarController.setShowTransparent(true /* transparent */);
+ }
+
+ boolean statusBarForcesShowingNavigation =
+ (mStatusBar.getAttrs().privateFlags
+ & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
+ boolean topAppHidesStatusBar = topAppHidesStatusBar();
+ if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent
+ || statusBarForcesShowingNavigation) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
+ if (mStatusBarController.setBarShowingLw(true)) {
+ changes |= FINISH_LAYOUT_REDO_LAYOUT;
+ }
+ // Maintain fullscreen layout until incoming animation is complete.
+ topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw();
+ // Transient status bar is not allowed if status bar is on lockscreen or status bar
+ // is expecting the navigation keys from the user.
+ if ((mForceStatusBarFromKeyguard || statusBarForcesShowingNavigation)
+ && mStatusBarController.isTransientShowing()) {
+ mStatusBarController.updateVisibilityLw(false /*transientAllowed*/,
+ mLastSystemUiFlags, mLastSystemUiFlags);
+ }
+ } else if (mTopFullscreenOpaqueWindowState != null) {
+ topIsFullscreen = topAppHidesStatusBar;
+ // The subtle difference between the window for mTopFullscreenOpaqueWindowState
+ // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
+ // has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the
+ // case though.
+ if (mStatusBarController.isTransientShowing()) {
+ if (mStatusBarController.setBarShowingLw(true)) {
+ changes |= FINISH_LAYOUT_REDO_LAYOUT;
+ }
+ } else if (topIsFullscreen
+ && !mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM)
+ && !mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
+ if (mStatusBarController.setBarShowingLw(false)) {
+ changes |= FINISH_LAYOUT_REDO_LAYOUT;
+ } else {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding");
+ }
+ } else {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen");
+ if (mStatusBarController.setBarShowingLw(true)) {
+ changes |= FINISH_LAYOUT_REDO_LAYOUT;
+ }
+ topAppHidesStatusBar = false;
+ }
+ }
+ mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar);
+ }
+
+ if (mTopIsFullscreen != topIsFullscreen) {
+ if (!topIsFullscreen) {
+ // Force another layout when status bar becomes fully shown.
+ changes |= FINISH_LAYOUT_REDO_LAYOUT;
+ }
+ mTopIsFullscreen = topIsFullscreen;
+ }
+
+ if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+ // If the navigation bar has been hidden or shown, we need to do another
+ // layout pass to update that window.
+ changes |= FINISH_LAYOUT_REDO_LAYOUT;
+ }
+
+ if (mShowingDream != mLastShowingDream) {
+ mLastShowingDream = mShowingDream;
+ mService.notifyShowingDreamChanged();
+ }
+
+ updateWindowSleepToken();
+
+ mService.mPolicy.setAllowLockscreenWhenOn(getDisplayId(), mAllowLockscreenWhenOn);
+ return changes;
+ }
+
+ private void updateWindowSleepToken() {
+ if (mWindowSleepTokenNeeded && !mLastWindowSleepTokenNeeded) {
+ mHandler.removeCallbacks(mReleaseSleepTokenRunnable);
+ mHandler.post(mAcquireSleepTokenRunnable);
+ } else if (!mWindowSleepTokenNeeded && mLastWindowSleepTokenNeeded) {
+ mHandler.removeCallbacks(mAcquireSleepTokenRunnable);
+ mHandler.post(mReleaseSleepTokenRunnable);
+ }
+ mLastWindowSleepTokenNeeded = mWindowSleepTokenNeeded;
+ }
+
+ /**
+ * @return Whether the top app should hide the statusbar based on the top fullscreen opaque
+ * window.
+ */
+ private boolean topAppHidesStatusBar() {
+ if (mTopFullscreenOpaqueWindowState == null) {
+ return false;
+ }
+ final int fl = PolicyControl.getWindowFlags(null,
+ mTopFullscreenOpaqueWindowState.getAttrs());
+ if (localLOGV) {
+ Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw());
+ Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+ + " lp.flags=0x" + Integer.toHexString(fl));
+ }
+ return (fl & LayoutParams.FLAG_FULLSCREEN) != 0
+ || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+ }
+
+ /**
+ * Called when the resource overlays change.
+ */
+ public void onOverlayChangedLw() {
+ onConfigurationChanged();
+ }
+
+ /**
+ * Called when the configuration has changed, and it's safe to load new values from resources.
+ */
+ public void onConfigurationChanged() {
+ final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+
+ final Context uiContext = getSystemUiContext();
+ final Resources res = uiContext.getResources();
+ final int portraitRotation = displayRotation.getPortraitRotation();
+ final int upsideDownRotation = displayRotation.getUpsideDownRotation();
+ final int landscapeRotation = displayRotation.getLandscapeRotation();
+ final int seascapeRotation = displayRotation.getSeascapeRotation();
+
+ mStatusBarHeightForRotation[portraitRotation] =
+ mStatusBarHeightForRotation[upsideDownRotation] =
+ res.getDimensionPixelSize(R.dimen.status_bar_height_portrait);
+ mStatusBarHeightForRotation[landscapeRotation] =
+ mStatusBarHeightForRotation[seascapeRotation] =
+ res.getDimensionPixelSize(R.dimen.status_bar_height_landscape);
+
+ // Height of the navigation bar when presented horizontally at bottom
+ mNavigationBarHeightForRotationDefault[portraitRotation] =
+ mNavigationBarHeightForRotationDefault[upsideDownRotation] =
+ res.getDimensionPixelSize(R.dimen.navigation_bar_height);
+ mNavigationBarHeightForRotationDefault[landscapeRotation] =
+ mNavigationBarHeightForRotationDefault[seascapeRotation] =
+ res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape);
+
+ // Width of the navigation bar when presented vertically along one side
+ mNavigationBarWidthForRotationDefault[portraitRotation] =
+ mNavigationBarWidthForRotationDefault[upsideDownRotation] =
+ mNavigationBarWidthForRotationDefault[landscapeRotation] =
+ mNavigationBarWidthForRotationDefault[seascapeRotation] =
+ res.getDimensionPixelSize(R.dimen.navigation_bar_width);
+
+ if (ALTERNATE_CAR_MODE_NAV_SIZE) {
+ // Height of the navigation bar when presented horizontally at bottom
+ mNavigationBarHeightForRotationInCarMode[portraitRotation] =
+ mNavigationBarHeightForRotationInCarMode[upsideDownRotation] =
+ res.getDimensionPixelSize(R.dimen.navigation_bar_height_car_mode);
+ mNavigationBarHeightForRotationInCarMode[landscapeRotation] =
+ mNavigationBarHeightForRotationInCarMode[seascapeRotation] =
+ res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape_car_mode);
+
+ // Width of the navigation bar when presented vertically along one side
+ mNavigationBarWidthForRotationInCarMode[portraitRotation] =
+ mNavigationBarWidthForRotationInCarMode[upsideDownRotation] =
+ mNavigationBarWidthForRotationInCarMode[landscapeRotation] =
+ mNavigationBarWidthForRotationInCarMode[seascapeRotation] =
+ res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode);
+ }
+
+ // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
+ mExperiments.onConfigurationChanged(uiContext);
+ // EXPERIMENT END
+ }
+
+ @VisibleForTesting
+ Context getSystemUiContext() {
+ final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+ return mDisplayContent.isDefaultDisplay
+ ? uiContext : uiContext.createDisplayContext(mDisplayContent.getDisplay());
+ }
+
+ private int getNavigationBarWidth(int rotation, int uiMode) {
+ if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+ return mNavigationBarWidthForRotationInCarMode[rotation];
+ } else {
+ return mNavigationBarWidthForRotationDefault[rotation];
+ }
+ }
+
+ /**
+ * Return the display width available after excluding any screen
+ * decorations that could never be removed in Honeycomb. That is, system bar or
+ * button bar.
+ */
+ public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
+ DisplayCutout displayCutout) {
+ int width = fullWidth;
+ if (hasNavigationBar()) {
+ // For a basic navigation bar, when we are in landscape mode we place
+ // the navigation bar to the side.
+ if (navigationBarCanMove() && fullWidth > fullHeight) {
+ width -= getNavigationBarWidth(rotation, uiMode);
+ }
+ }
+ if (displayCutout != null) {
+ width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
+ }
+ return width;
+ }
+
+ private int getNavigationBarHeight(int rotation, int uiMode) {
+ if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+ return mNavigationBarHeightForRotationInCarMode[rotation];
+ } else {
+ return mNavigationBarHeightForRotationDefault[rotation];
+ }
+ }
+
+ /**
+ * Return the display height available after excluding any screen
+ * decorations that could never be removed in Honeycomb. That is, system bar or
+ * button bar.
+ */
+ public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
+ DisplayCutout displayCutout) {
+ int height = fullHeight;
+ if (hasNavigationBar()) {
+ // For a basic navigation bar, when we are in portrait mode we place
+ // the navigation bar to the bottom.
+ if (!navigationBarCanMove() || fullWidth < fullHeight) {
+ height -= getNavigationBarHeight(rotation, uiMode);
+ }
+ }
+ if (displayCutout != null) {
+ height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();
+ }
+ return height;
+ }
+
+ /**
+ * Return the available screen width that we should report for the
+ * configuration. This must be no larger than
+ * {@link #getNonDecorDisplayWidth(int, int, int, int, DisplayCutout)}; it may be smaller
+ * than that to account for more transient decoration like a status bar.
+ */
+ public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
+ DisplayCutout displayCutout) {
+ return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayCutout);
+ }
+
+ /**
+ * Return the available screen height that we should report for the
+ * configuration. This must be no larger than
+ * {@link #getNonDecorDisplayHeight(int, int, int, int, DisplayCutout)}; it may be smaller
+ * than that to account for more transient decoration like a status bar.
+ */
+ public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
+ DisplayCutout displayCutout) {
+ // There is a separate status bar at the top of the display. We don't count that as part
+ // of the fixed decor, since it can hide; however, for purposes of configurations,
+ // we do want to exclude it since applications can't generally use that part
+ // of the screen.
+ int statusBarHeight = mStatusBarHeightForRotation[rotation];
+ if (displayCutout != null) {
+ // If there is a cutout, it may already have accounted for some part of the status
+ // bar height.
+ statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
+ }
+ return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayCutout)
+ - statusBarHeight;
+ }
+
+ boolean isShowingDreamLw() {
+ return mShowingDream;
+ }
+
+ /**
+ * Calculates the stable insets without running a layout.
+ *
+ * @param displayRotation the current display rotation
+ * @param displayWidth the current display width
+ * @param displayHeight the current display height
+ * @param displayCutout the current display cutout
+ * @param outInsets the insets to return
+ */
+ public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+ DisplayCutout displayCutout, Rect outInsets) {
+ outInsets.setEmpty();
+
+ // Navigation bar and status bar.
+ getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets);
+ outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]);
+ }
+
+ /**
+ * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
+ * bar or button bar. See {@link #getNonDecorDisplayWidth}.
+ *
+ * @param displayRotation the current display rotation
+ * @param displayWidth the current display width
+ * @param displayHeight the current display height
+ * @param displayCutout the current display cutout
+ * @param outInsets the insets to return
+ */
+ public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+ DisplayCutout displayCutout, Rect outInsets) {
+ outInsets.setEmpty();
+
+ // Only navigation bar
+ if (hasNavigationBar()) {
+ final int uiMode = mService.mPolicy.getUiMode();
+ int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
+ if (position == NAV_BAR_BOTTOM) {
+ outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode);
+ } else if (position == NAV_BAR_RIGHT) {
+ outInsets.right = getNavigationBarWidth(displayRotation, uiMode);
+ } else if (position == NAV_BAR_LEFT) {
+ outInsets.left = getNavigationBarWidth(displayRotation, uiMode);
+ }
+ }
+
+ if (displayCutout != null) {
+ outInsets.left += displayCutout.getSafeInsetLeft();
+ outInsets.top += displayCutout.getSafeInsetTop();
+ outInsets.right += displayCutout.getSafeInsetRight();
+ outInsets.bottom += displayCutout.getSafeInsetBottom();
+ }
+ }
+
+ @NavigationBarPosition
+ int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
+ if (navigationBarCanMove() && displayWidth > displayHeight) {
+ if (displayRotation == Surface.ROTATION_270) {
+ return NAV_BAR_LEFT;
+ } else if (displayRotation == Surface.ROTATION_90) {
+ return NAV_BAR_RIGHT;
+ }
+ }
+ return NAV_BAR_BOTTOM;
+ }
+
+ /**
+ * @return The side of the screen where navigation bar is positioned.
+ * @see WindowManagerPolicyConstants#NAV_BAR_LEFT
+ * @see WindowManagerPolicyConstants#NAV_BAR_RIGHT
+ * @see WindowManagerPolicyConstants#NAV_BAR_BOTTOM
+ */
+ @NavigationBarPosition
+ public int getNavBarPosition() {
+ return mNavigationBarPosition;
+ }
+
+ /**
+ * A new window has been focused.
+ */
+ public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
+ mFocusedWindow = newFocus;
+ mLastFocusedWindow = lastFocus;
+ if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+ // If the navigation bar has been hidden or shown, we need to do another
+ // layout pass to update that window.
+ return FINISH_LAYOUT_REDO_LAYOUT;
+ }
+ return 0;
+ }
+
+ /**
+ * Return true if it is okay to perform animations for an app transition
+ * that is about to occur. You may return false for this if, for example,
+ * the dream window is currently displayed so the switch should happen
+ * immediately.
+ */
+ public boolean allowAppAnimationsLw() {
+ return !mShowingDream;
+ }
+
+ private void updateDreamingSleepToken(boolean acquire) {
+ if (acquire) {
+ final int displayId = getDisplayId();
+ if (mDreamingSleepToken == null) {
+ mDreamingSleepToken = mService.mAtmInternal.acquireSleepToken(
+ "DreamOnDisplay" + displayId, displayId);
+ }
+ } else {
+ if (mDreamingSleepToken != null) {
+ mDreamingSleepToken.release();
+ mDreamingSleepToken = null;
+ }
+ }
+ }
+
+ private void requestTransientBars(WindowState swipeTarget) {
+ synchronized (mLock) {
+ if (!mService.mPolicy.isUserSetupComplete()) {
+ // Swipe-up for navigation bar is disabled during setup
+ return;
+ }
+ boolean sb = mStatusBarController.checkShowTransientBarLw();
+ boolean nb = mNavigationBarController.checkShowTransientBarLw()
+ && !isNavBarEmpty(mLastSystemUiFlags);
+ if (sb || nb) {
+ // Don't show status bar when swiping on already visible navigation bar
+ if (!nb && swipeTarget == mNavigationBar) {
+ if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
+ return;
+ }
+ if (sb) mStatusBarController.showTransient();
+ if (nb) mNavigationBarController.showTransient();
+ mImmersiveModeConfirmation.confirmCurrentPrompt();
+ updateSystemUiVisibilityLw();
+ }
+ }
+ }
+
+ private void disposeInputConsumer(InputConsumer inputConsumer) {
+ if (inputConsumer != null) {
+ inputConsumer.dismiss();
+ }
+ }
+
+ private boolean isStatusBarKeyguard() {
+ return mStatusBar != null
+ && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
+ }
+
+ private boolean isKeyguardOccluded() {
+ // TODO (b/113840485): Handle per display keyguard.
+ return mService.mPolicy.isKeyguardOccluded();
+ }
+
+ void resetSystemUiVisibilityLw() {
+ mLastSystemUiFlags = 0;
+ updateSystemUiVisibilityLw();
+ }
+
+ private int updateSystemUiVisibilityLw() {
+ // If there is no window focused, there will be nobody to handle the events
+ // anyway, so just hang on in whatever state we're in until things settle down.
+ WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
+ : mTopFullscreenOpaqueWindowState;
+ if (winCandidate == null) {
+ return 0;
+ }
+
+ // The immersive mode confirmation should never affect the system bar visibility, otherwise
+ // it will unhide the navigation bar and hide itself.
+ if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
+
+ // The immersive mode confirmation took the focus from mLastFocusedWindow which was
+ // controlling the system ui visibility. So if mLastFocusedWindow can still receive
+ // keys, we let it keep controlling the visibility.
+ final boolean lastFocusCanReceiveKeys =
+ (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
+ winCandidate = isStatusBarKeyguard() ? mStatusBar
+ : lastFocusCanReceiveKeys ? mLastFocusedWindow
+ : mTopFullscreenOpaqueWindowState;
+ if (winCandidate == null) {
+ return 0;
+ }
+ }
+ final WindowState win = winCandidate;
+ if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && isKeyguardOccluded()) {
+ // We are updating at a point where the keyguard has gotten
+ // focus, but we were last in a state where the top window is
+ // hiding it. This is probably because the keyguard as been
+ // shown while the top window was displayed, so we want to ignore
+ // it here because this is just a very transient change and it
+ // will quickly lose focus once it correctly gets hidden.
+ return 0;
+ }
+
+ int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
+ & ~mResettingSystemUiFlags
+ & ~mForceClearedSystemUiFlags;
+ if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
+ tmpVisibility
+ &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
+ }
+
+ final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
+ mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
+ final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
+ mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
+ mService.getStackBounds(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
+ mService.getStackBounds(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
+ final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
+ final int diff = visibility ^ mLastSystemUiFlags;
+ final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
+ final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
+ final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
+ if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
+ && mFocusedApp == win.getAppToken()
+ && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
+ && mLastDockedStackBounds.equals(mDockedStackBounds)) {
+ return 0;
+ }
+ mLastSystemUiFlags = visibility;
+ mLastFullscreenStackSysUiFlags = fullscreenVisibility;
+ mLastDockedStackSysUiFlags = dockedVisibility;
+ mLastFocusNeedsMenu = needsMenu;
+ mFocusedApp = win.getAppToken();
+ final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
+ final Rect dockedStackBounds = new Rect(mDockedStackBounds);
+ mHandler.post(() -> {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ final int displayId = getDisplayId();
+ statusBar.setSystemUiVisibility(displayId, visibility, fullscreenVisibility,
+ dockedVisibility, 0xffffffff, fullscreenStackBounds,
+ dockedStackBounds, win.toString());
+ statusBar.topAppWindowChanged(displayId, needsMenu);
+ }
+ });
+ return diff;
+ }
+
+ private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
+ final boolean onKeyguard = isStatusBarKeyguard() && !isKeyguardOccluded();
+ final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming;
+ if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) {
+ // If the top fullscreen-or-dimming window is also the top fullscreen, respect
+ // its light flag.
+ vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
+ & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ } else if (statusColorWin != null && statusColorWin.isDimming()) {
+ // Otherwise if it's dimming, clear the light flag.
+ vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ }
+ return vis;
+ }
+
+ @VisibleForTesting
+ @Nullable
+ static WindowState chooseNavigationColorWindowLw(WindowState opaque,
+ WindowState opaqueOrDimming, WindowState imeWindow,
+ @NavigationBarPosition int navBarPosition) {
+ // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME
+ // window can be navigation color window.
+ final boolean imeWindowCanNavColorWindow = imeWindow != null
+ && imeWindow.isVisibleLw()
+ && navBarPosition == NAV_BAR_BOTTOM
+ && (PolicyControl.getWindowFlags(imeWindow, null)
+ & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+
+ if (opaque != null && opaqueOrDimming == opaque) {
+ // If the top fullscreen-or-dimming window is also the top fullscreen, respect it
+ // unless IME window is also eligible, since currently the IME window is always show
+ // above the opaque fullscreen app window, regardless of the IME target window.
+ // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.
+ return imeWindowCanNavColorWindow ? imeWindow : opaque;
+ }
+
+ if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {
+ // No dimming window is involved. Determine the result only with the IME window.
+ return imeWindowCanNavColorWindow ? imeWindow : null;
+ }
+
+ if (!imeWindowCanNavColorWindow) {
+ // No IME window is involved. Determine the result only with opaqueOrDimming.
+ return opaqueOrDimming;
+ }
+
+ // The IME window and the dimming window are competing. Check if the dimming window can be
+ // IME target or not.
+ if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) {
+ // The IME window is above the dimming window.
+ return imeWindow;
+ } else {
+ // The dimming window is above the IME window.
+ return opaqueOrDimming;
+ }
+ }
+
+ @VisibleForTesting
+ static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming,
+ WindowState imeWindow, WindowState navColorWin) {
+
+ if (navColorWin != null) {
+ if (navColorWin == imeWindow || navColorWin == opaque) {
+ // Respect the light flag.
+ vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+ vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
+ & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+ } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
+ // Clear the light flag for dimming window.
+ vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+ }
+ }
+ return vis;
+ }
+
+ private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
+ final boolean dockedStackVisible =
+ mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ final boolean freeformStackVisible =
+ mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM);
+ final boolean resizing = mDisplayContent.getDockedDividerController().isResizing();
+
+ // We need to force system bars when the docked stack is visible, when the freeform stack
+ // is visible but also when we are resizing for the transitions when docked stack
+ // visibility changes.
+ mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
+ final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
+
+ // apply translucent bar vis flags
+ WindowState fullscreenTransWin = isStatusBarKeyguard() && !isKeyguardOccluded()
+ ? mStatusBar
+ : mTopFullscreenOpaqueWindowState;
+ vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
+ vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
+ final int dockedVis = mStatusBarController.applyTranslucentFlagLw(
+ mTopDockedOpaqueWindowState, 0, 0);
+
+ final boolean fullscreenDrawsStatusBarBackground =
+ drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);
+ final boolean dockedDrawsStatusBarBackground =
+ drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);
+
+ // prevent status bar interaction from clearing certain flags
+ int type = win.getAttrs().type;
+ boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
+ if (statusBarHasFocus && !isStatusBarKeyguard()) {
+ int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_IMMERSIVE
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ if (isKeyguardOccluded()) {
+ flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
+ }
+ vis = (vis & ~flags) | (oldVis & flags);
+ }
+
+ if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
+ vis |= View.STATUS_BAR_TRANSPARENT;
+ vis &= ~View.STATUS_BAR_TRANSLUCENT;
+ } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)
+ || forceOpaqueStatusBar) {
+ vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
+ }
+
+ vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);
+
+ // update status bar
+ boolean immersiveSticky =
+ (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
+ final boolean hideStatusBarWM =
+ mTopFullscreenOpaqueWindowState != null
+ && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
+ & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
+ final boolean hideStatusBarSysui =
+ (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+ final boolean hideNavBarSysui =
+ (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
+
+ final boolean transientStatusBarAllowed = mStatusBar != null
+ && (statusBarHasFocus || (!mForceShowSystemBars
+ && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
+
+ final boolean transientNavBarAllowed = mNavigationBar != null
+ && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;
+
+ final long now = SystemClock.uptimeMillis();
+ final boolean pendingPanic = mPendingPanicGestureUptime != 0
+ && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
+ final DisplayPolicy defaultDisplayPolicy =
+ mService.getDefaultDisplayContentLocked().getDisplayPolicy();
+ if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard()
+ // TODO (b/111955725): Show keyguard presentation on all external displays
+ && defaultDisplayPolicy.isKeyguardDrawComplete()) {
+ // The user performed the panic gesture recently, we're about to hide the bars,
+ // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
+ mPendingPanicGestureUptime = 0;
+ mStatusBarController.showTransient();
+ if (!isNavBarEmpty(vis)) {
+ mNavigationBarController.showTransient();
+ }
+ }
+
+ final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
+ && !transientStatusBarAllowed && hideStatusBarSysui;
+ final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
+ && !transientNavBarAllowed;
+ if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {
+ // clear the clearable flags instead
+ clearClearableFlagsLw();
+ vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
+ }
+
+ final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
+ immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
+ final boolean navAllowedHidden = immersive || immersiveSticky;
+
+ if (hideNavBarSysui && !navAllowedHidden
+ && mService.mPolicy.getWindowLayerLw(win)
+ > mService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) {
+ // We can't hide the navbar from this window otherwise the input consumer would not get
+ // the input events.
+ vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+ }
+
+ vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);
+
+ // update navigation bar
+ boolean oldImmersiveMode = isImmersiveMode(oldVis);
+ boolean newImmersiveMode = isImmersiveMode(vis);
+ if (oldImmersiveMode != newImmersiveMode) {
+ final String pkg = win.getOwningPackage();
+ mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
+ mService.mPolicy.isUserSetupComplete(),
+ isNavBarEmpty(win.getSystemUiVisibility()));
+ }
+
+ vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
+
+ final WindowState navColorWin = chooseNavigationColorWindowLw(
+ mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
+ mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
+ vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
+ mTopFullscreenOpaqueOrDimmingWindowState,
+ mDisplayContent.mInputMethodWindow, navColorWin);
+
+ return vis;
+ }
+
+ private boolean drawsStatusBarBackground(int vis, WindowState win) {
+ if (!mStatusBarController.isTransparentAllowed(win)) {
+ return false;
+ }
+ if (win == null) {
+ return true;
+ }
+
+ final boolean drawsSystemBars =
+ (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+ final boolean forceDrawsSystemBars =
+ (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
+
+ return forceDrawsSystemBars || drawsSystemBars && (vis & View.STATUS_BAR_TRANSLUCENT) == 0;
+ }
+
+ /**
+ * @return the current visibility flags with the nav-bar opacity related flags toggled based
+ * on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
+ */
+ private int configureNavBarOpacity(int visibility, boolean dockedStackVisible,
+ boolean freeformStackVisible, boolean isDockedDividerResizing) {
+ if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
+ if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) {
+ visibility = setNavBarOpaqueFlag(visibility);
+ }
+ } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
+ if (isDockedDividerResizing) {
+ visibility = setNavBarOpaqueFlag(visibility);
+ } else if (freeformStackVisible) {
+ visibility = setNavBarTranslucentFlag(visibility);
+ } else {
+ visibility = setNavBarOpaqueFlag(visibility);
+ }
+ }
+
+ if (!areTranslucentBarsAllowed()) {
+ visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT;
+ }
+ return visibility;
+ }
+
+ private int setNavBarOpaqueFlag(int visibility) {
+ return visibility & ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT);
+ }
+
+ private int setNavBarTranslucentFlag(int visibility) {
+ visibility &= ~View.NAVIGATION_BAR_TRANSPARENT;
+ return visibility | View.NAVIGATION_BAR_TRANSLUCENT;
+ }
+
+ private void clearClearableFlagsLw() {
+ int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS;
+ if (newVal != mResettingSystemUiFlags) {
+ mResettingSystemUiFlags = newVal;
+ mDisplayContent.reevaluateStatusBarVisibility();
+ }
+ }
+
+ private boolean isImmersiveMode(int vis) {
+ final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ return mNavigationBar != null
+ && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
+ && (vis & flags) != 0
+ && canHideNavigationBar();
+ }
+
+ /**
+ * @return whether the navigation bar can be hidden, e.g. the device has a navigation bar
+ */
+ private boolean canHideNavigationBar() {
+ return hasNavigationBar();
+ }
+
+ private static boolean isNavBarEmpty(int systemUiFlags) {
+ final int disableNavigationBar = (View.STATUS_BAR_DISABLE_HOME
+ | View.STATUS_BAR_DISABLE_BACK
+ | View.STATUS_BAR_DISABLE_RECENT);
+
+ return (systemUiFlags & disableNavigationBar) == disableNavigationBar;
+ }
+
+ /**
+ * @return whether the navigation or status bar can be made translucent
+ *
+ * This should return true unless touch exploration is not enabled or
+ * R.boolean.config_enableTranslucentDecor is false.
+ */
+ private boolean areTranslucentBarsAllowed() {
+ return mTranslucentDecorEnabled;
+ }
+
+ boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation,
+ int newRotation) {
+ // For the upside down rotation we don't rotate seamlessly as the navigation
+ // bar moves position.
+ // Note most apps (using orientation:sensor or user as opposed to fullSensor)
+ // will not enter the reverse portrait orientation, so actually the
+ // orientation won't change at all.
+ if (oldRotation == displayRotation.getUpsideDownRotation()
+ || newRotation == displayRotation.getUpsideDownRotation()) {
+ return false;
+ }
+ // If the navigation bar can't change sides, then it will
+ // jump when we change orientations and we don't rotate
+ // seamlessly.
+ if (!navigationBarCanMove()) {
+ return false;
+ }
+
+ final WindowState w = mTopFullscreenOpaqueWindowState;
+ if (w != mFocusedWindow) {
+ return false;
+ }
+
+ // We only enable seamless rotation if the top window has requested
+ // it and is in the fullscreen opaque state. Seamless rotation
+ // requires freezing various Surface states and won't work well
+ // with animations, so we disable it in the animation case for now.
+ if (w != null && !w.isAnimatingLw()
+ && w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) {
+ return true;
+ }
+ return false;
+ }
+
+ private final Runnable mHiddenNavPanic = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ if (!mService.mPolicy.isUserSetupComplete()) {
+ // Swipe-up for navigation bar is disabled during setup
+ return;
+ }
+ mPendingPanicGestureUptime = SystemClock.uptimeMillis();
+ if (!isNavBarEmpty(mLastSystemUiFlags)) {
+ mNavigationBarController.showTransient();
+ }
+ }
+ }
+ };
+
+ void onPowerKeyDown(boolean isScreenOn) {
+ // Detect user pressing the power button in panic when an application has
+ // taken over the whole screen.
+ boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn,
+ SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags),
+ isNavBarEmpty(mLastSystemUiFlags));
+ if (panic) {
+ mHandler.post(mHiddenNavPanic);
+ }
+ }
+
+ void onVrStateChangedLw(boolean enabled) {
+ mImmersiveModeConfirmation.onVrStateChangedLw(enabled);
+ }
+
+ /**
+ * Called when the state of lock task mode changes. This should be used to disable immersive
+ * mode confirmation.
+ *
+ * @param lockTaskState the new lock task mode state. One of
+ * {@link ActivityManager#LOCK_TASK_MODE_NONE},
+ * {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
+ * {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
+ */
+ public void onLockTaskStateChangedLw(int lockTaskState) {
+ mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState);
+ }
+
+ /**
+ * Request a screenshot be taken.
+ *
+ * @param screenshotType The type of screenshot, for example either
+ * {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or
+ * {@link WindowManager#TAKE_SCREENSHOT_SELECTED_REGION}
+ */
+ public void takeScreenshot(int screenshotType) {
+ if (mScreenshotHelper != null) {
+ mScreenshotHelper.takeScreenshot(screenshotType,
+ mStatusBar != null && mStatusBar.isVisibleLw(),
+ mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler);
+ }
+ }
+
void dump(String prefix, PrintWriter pw) {
- pw.println(prefix + "DisplayPolicy");
- pw.print(prefix + " mCarDockEnablesAccelerometer=" + mCarDockEnablesAccelerometer);
- pw.println(" mDeskDockEnablesAccelerometer=" + mDeskDockEnablesAccelerometer);
- pw.print(prefix + " mDockMode=" + Intent.dockStateToString(mDockMode));
- pw.println(" mLidState=" + WindowManagerFuncs.lidStateToString(mLidState));
- pw.print(prefix + " mAwake=" + mAwake);
- pw.print(" mScreenOnEarly=" + mScreenOnEarly);
- pw.println(" mScreenOnFully=" + mScreenOnFully);
- pw.print(prefix + " mKeyguardDrawComplete=" + mKeyguardDrawComplete);
- pw.println(" mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
- pw.println(prefix + " mHdmiPlugged=" + mHdmiPlugged);
+ pw.print(prefix); pw.print("DisplayPolicy");
+ prefix += " ";
+ pw.print(prefix);
+ pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer);
+ pw.print(" mDeskDockEnablesAccelerometer=");
+ pw.println(mDeskDockEnablesAccelerometer);
+ pw.print(prefix); pw.print("mDockMode="); pw.print(Intent.dockStateToString(mDockMode));
+ pw.print(" mLidState="); pw.println(WindowManagerFuncs.lidStateToString(mLidState));
+ pw.print(prefix); pw.print("mAwake="); pw.print(mAwake);
+ pw.print(" mScreenOnEarly="); pw.print(mScreenOnEarly);
+ pw.print(" mScreenOnFully="); pw.println(mScreenOnFully);
+ pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
+ pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
+ pw.print(prefix); pw.print("mHdmiPlugged="); pw.println(mHdmiPlugged);
+ if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0
+ || mForceClearedSystemUiFlags != 0) {
+ pw.print(prefix); pw.print("mLastSystemUiFlags=0x");
+ pw.print(Integer.toHexString(mLastSystemUiFlags));
+ pw.print(" mResettingSystemUiFlags=0x");
+ pw.print(Integer.toHexString(mResettingSystemUiFlags));
+ pw.print(" mForceClearedSystemUiFlags=0x");
+ pw.println(Integer.toHexString(mForceClearedSystemUiFlags));
+ }
+ if (mLastFocusNeedsMenu) {
+ pw.print(prefix); pw.print("mLastFocusNeedsMenu="); pw.println(mLastFocusNeedsMenu);
+ }
+ pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
+ pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
+ pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
+ if (mStatusBar != null) {
+ pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar);
+ pw.print(" isStatusBarKeyguard="); pw.println(isStatusBarKeyguard());
+ }
+ if (mNavigationBar != null) {
+ pw.print(prefix); pw.print("mNavigationBar="); pw.println(mNavigationBar);
+ }
+ if (mFocusedWindow != null) {
+ pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow);
+ }
+ if (mFocusedApp != null) {
+ pw.print(prefix); pw.print("mFocusedApp="); pw.println(mFocusedApp);
+ }
+ if (mTopFullscreenOpaqueWindowState != null) {
+ pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState=");
+ pw.println(mTopFullscreenOpaqueWindowState);
+ }
+ if (mTopFullscreenOpaqueOrDimmingWindowState != null) {
+ pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState=");
+ pw.println(mTopFullscreenOpaqueOrDimmingWindowState);
+ }
+ if (mForcingShowNavBar) {
+ pw.print(prefix); pw.print("mForcingShowNavBar="); pw.println(mForcingShowNavBar);
+ pw.print(prefix); pw.print("mForcingShowNavBarLayer=");
+ pw.println(mForcingShowNavBarLayer);
+ }
+ pw.print(prefix); pw.print("mTopIsFullscreen="); pw.print(mTopIsFullscreen);
+ pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
+ pw.print(" mForceStatusBarFromKeyguard="); pw.println(mForceStatusBarFromKeyguard);
+ pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
+ mStatusBarController.dump(pw, prefix);
+ mNavigationBarController.dump(pw, prefix);
+
+ pw.print(prefix); pw.println("Looper state:");
+ mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6ab7090..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();
@@ -844,9 +883,9 @@
pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
- pw.print(prefix + " mSupportAutoRotation=" + mSupportAutoRotation);
+ pw.println(prefix + " mSupportAutoRotation=" + mSupportAutoRotation);
if (mOrientationListener != null) {
- pw.print(" mOrientationSensorEnabled=" + mOrientationListener.mEnabled);
+ mOrientationListener.dump(pw, prefix + " ");
}
pw.println();
@@ -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/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/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index c377072..7ea88bb 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -28,6 +28,8 @@
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
@@ -52,6 +54,7 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
import com.android.server.LocalServices;
@@ -184,8 +187,8 @@
.calculateNonDismissingSnapTarget(position).position;
DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect,
mTmpRect2.width(), mTmpRect2.height(), getContentWidth());
- mService.mPolicy.getStableInsetsLw(rotation, mTmpRect2.width(), mTmpRect2.height(),
- displayCutout, mTmpRect3);
+ mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, mTmpRect2.width(),
+ mTmpRect2.height(), displayCutout, mTmpRect3);
mService.intersectDisplayInsetBounds(mTmpRect2, mTmpRect3, mTmpRect);
minWidth = Math.min(mTmpRect.width(), minWidth);
}
@@ -231,8 +234,9 @@
final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
final int displayWidth = parentConfig.windowConfiguration.getBounds().width();
final int displayHeight = parentConfig.windowConfiguration.getBounds().height();
- mService.mPolicy.getStableInsetsLw(parentConfig.windowConfiguration.getRotation(),
- displayWidth, displayHeight, displayCutout, mTmpRect);
+ mDisplayContent.getDisplayPolicy().getStableInsetsLw(
+ parentConfig.windowConfiguration.getRotation(), displayWidth, displayHeight,
+ displayCutout, mTmpRect);
int dividerSize = mDividerWindowWidth - 2 * mDividerInsets;
// The offset in the left (landscape)/top (portrait) is calculated with the minimized
// offset value with the divider size and any system insets in that direction.
@@ -240,7 +244,7 @@
outBounds.set(0, mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top,
displayWidth, displayHeight);
} else {
- // In landscape also inset the left/right side with the statusbar height to match the
+ // In landscape also inset the left/right side with the status bar height to match the
// minimized size height in portrait mode.
final int primaryTaskWidth = mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top;
int left = mTmpRect.left;
@@ -278,16 +282,16 @@
: mDisplayContent.mBaseDisplayHeight;
final DisplayCutout displayCutout =
mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout();
- mService.mPolicy.getStableInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
+ final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+ displayPolicy.getStableInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
config.unset();
config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
- final int displayId = mDisplayContent.getDisplayId();
- final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation,
- baseConfig.uiMode, displayId, displayCutout);
- final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
- baseConfig.uiMode, displayId, displayCutout);
- mService.mPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
+ final int appWidth = displayPolicy.getNonDecorDisplayWidth(dw, dh, rotation,
+ baseConfig.uiMode, displayCutout);
+ final int appHeight = displayPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
+ baseConfig.uiMode, displayCutout);
+ displayPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
final int leftInset = mTmpRect.left;
final int topInset = mTmpRect.top;
@@ -295,10 +299,10 @@
leftInset + appWidth /*right*/, topInset + appHeight /*bottom*/);
final float density = mDisplayContent.getDisplayMetrics().density;
- config.screenWidthDp = (int) (mService.mPolicy.getConfigDisplayWidth(dw, dh,
- rotation, baseConfig.uiMode, displayId, displayCutout) / density);
- config.screenHeightDp = (int) (mService.mPolicy.getConfigDisplayHeight(dw, dh,
- rotation, baseConfig.uiMode, displayId, displayCutout) / density);
+ config.screenWidthDp = (int) (displayPolicy.getConfigDisplayWidth(dw, dh, rotation,
+ baseConfig.uiMode, displayCutout) / density);
+ config.screenHeightDp = (int) (displayPolicy.getConfigDisplayHeight(dw, dh, rotation,
+ baseConfig.uiMode, displayCutout) / density);
final Context rotationContext = mService.mContext.createConfigurationContext(config);
mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm(
rotationContext.getResources(), dw, dh, getContentWidth(),
@@ -464,8 +468,32 @@
* @return true if the side provided is valid
*/
boolean canPrimaryStackDockTo(int dockSide, Rect parentRect, int rotation) {
- return mService.mPolicy.isDockSideAllowed(dockSide, mOriginalDockedSide,
- parentRect.width(), parentRect.height(), rotation);
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ return isDockSideAllowed(dockSide, mOriginalDockedSide,
+ policy.navigationBarPosition(parentRect.width(), parentRect.height(), rotation),
+ policy.navigationBarCanMove());
+ }
+
+ @VisibleForTesting
+ static boolean isDockSideAllowed(int dockSide, int originalDockSide, int navBarPosition,
+ boolean navigationBarCanMove) {
+ if (dockSide == DOCKED_TOP) {
+ return true;
+ }
+
+ if (navigationBarCanMove) {
+ // Only allow the dockside opposite to the nav bar position in landscape
+ return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT
+ || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT;
+ }
+
+ // Side is the same as original side
+ if (dockSide == originalDockSide) {
+ return true;
+ }
+
+ // Only if original docked side was top in portrait will allow left for landscape
+ return dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP;
}
void notifyDockedStackExistsChanged(boolean exists) {
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 7ed078a..e3ddadc 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -74,8 +74,15 @@
return mDragState != null;
}
- 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..8f6ed85 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -32,9 +32,11 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
+import android.graphics.Rect;
import android.graphics.Point;
import android.hardware.input.InputManager;
import android.os.Build;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
@@ -118,6 +120,11 @@
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();
+
DragState(WindowManagerService service, DragDropController controller, IBinder token,
SurfaceControl surface, int flags, IBinder localWin) {
mService = service;
@@ -127,6 +134,42 @@
mFlags = flags;
mLocalWin = localWin;
mNotifiedWindows = new ArrayList<WindowState>();
+
+ }
+
+ 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);
}
/**
@@ -218,7 +261,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 +269,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/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
similarity index 81%
rename from services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
rename to services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 4aa2446..3d20501 100644
--- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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,10 +14,11 @@
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.wm;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.view.Display.DEFAULT_DISPLAY;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
@@ -32,16 +33,14 @@
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.service.vr.IVrManager;
-import android.service.vr.IVrStateCallbacks;
import android.util.DisplayMetrics;
import android.util.Slog;
+import android.view.Display;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
@@ -55,7 +54,6 @@
import android.widget.FrameLayout;
import com.android.internal.R;
-import com.android.server.vr.VrManagerService;
/**
* Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden
@@ -67,30 +65,34 @@
private static final boolean DEBUG_SHOW_EVERY_TIME = false; // super annoying, use with caution
private static final String CONFIRMED = "confirmed";
+ private static boolean sConfirmed;
+
private final Context mContext;
private final H mHandler;
private final long mShowDelayMs;
private final long mPanicThresholdMs;
private final IBinder mWindowToken = new Binder();
- private boolean mConfirmed;
private ClingWindowView mClingWindow;
private long mPanicTime;
private WindowManager mWindowManager;
- private int mCurrentUserId;
// Local copy of vr mode enabled state, to avoid calling into VrManager with
// the lock held.
- boolean mVrModeEnabled = false;
+ private boolean mVrModeEnabled;
private int mLockTaskState = LOCK_TASK_MODE_NONE;
- public ImmersiveModeConfirmation(Context context) {
- mContext = ActivityThread.currentActivityThread().getSystemUiContext();
- mHandler = new H();
+ ImmersiveModeConfirmation(Context context, Looper looper, boolean vrModeEnabled) {
+ final Display display = context.getDisplay();
+ final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+ mContext = display.getDisplayId() == DEFAULT_DISPLAY
+ ? uiContext : uiContext.createDisplayContext(display);
+ mHandler = new H(looper);
mShowDelayMs = getNavBarExitDuration() * 3;
mPanicThresholdMs = context.getResources()
.getInteger(R.integer.config_immersive_mode_confirmation_panic);
mWindowManager = (WindowManager)
mContext.getSystemService(Context.WINDOW_SERVICE);
+ mVrModeEnabled = vrModeEnabled;
}
private long getNavBarExitDuration() {
@@ -98,57 +100,46 @@
return exit != null ? exit.getDuration() : 0;
}
- public void loadSetting(int currentUserId) {
- mConfirmed = false;
- mCurrentUserId = currentUserId;
- if (DEBUG) Slog.d(TAG, String.format("loadSetting() mCurrentUserId=%d", mCurrentUserId));
+ static boolean loadSetting(int currentUserId, Context context) {
+ final boolean wasConfirmed = sConfirmed;
+ sConfirmed = false;
+ if (DEBUG) Slog.d(TAG, String.format("loadSetting() currentUserId=%d", currentUserId));
String value = null;
try {
- value = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ value = Settings.Secure.getStringForUser(context.getContentResolver(),
Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
UserHandle.USER_CURRENT);
- mConfirmed = CONFIRMED.equals(value);
- if (DEBUG) Slog.d(TAG, "Loaded mConfirmed=" + mConfirmed);
+ sConfirmed = CONFIRMED.equals(value);
+ if (DEBUG) Slog.d(TAG, "Loaded sConfirmed=" + sConfirmed);
} catch (Throwable t) {
Slog.w(TAG, "Error loading confirmations, value=" + value, t);
}
+ return sConfirmed != wasConfirmed;
}
- private void saveSetting() {
+ private static void saveSetting(Context context) {
if (DEBUG) Slog.d(TAG, "saveSetting()");
try {
- final String value = mConfirmed ? CONFIRMED : null;
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ final String value = sConfirmed ? CONFIRMED : null;
+ Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
value,
UserHandle.USER_CURRENT);
if (DEBUG) Slog.d(TAG, "Saved value=" + value);
} catch (Throwable t) {
- Slog.w(TAG, "Error saving confirmations, mConfirmed=" + mConfirmed, t);
+ Slog.w(TAG, "Error saving confirmations, sConfirmed=" + sConfirmed, t);
}
}
- void systemReady() {
- IVrManager vrManager = IVrManager.Stub.asInterface(
- ServiceManager.getService(Context.VR_SERVICE));
- if (vrManager != null) {
- try {
- vrManager.registerListener(mVrStateCallbacks);
- mVrModeEnabled = vrManager.getVrModeState();
- } catch (RemoteException re) {
- }
- }
- }
-
- public void immersiveModeChangedLw(String pkg, boolean isImmersiveMode,
+ void immersiveModeChangedLw(String pkg, boolean isImmersiveMode,
boolean userSetupComplete, boolean navBarEmpty) {
mHandler.removeMessages(H.SHOW);
if (isImmersiveMode) {
final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg);
- if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s mConfirmed=%s",
- disabled, mConfirmed));
+ if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s sConfirmed=%s",
+ disabled, sConfirmed));
if (!disabled
- && (DEBUG_SHOW_EVERY_TIME || !mConfirmed)
+ && (DEBUG_SHOW_EVERY_TIME || !sConfirmed)
&& userSetupComplete
&& !mVrModeEnabled
&& !navBarEmpty
@@ -161,7 +152,7 @@
}
}
- public boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode,
+ boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode,
boolean navBarEmpty) {
if (!isScreenOn && (time - mPanicTime < mPanicThresholdMs)) {
// turning the screen back on within the panic threshold
@@ -176,7 +167,7 @@
return false;
}
- public void confirmCurrentPrompt() {
+ void confirmCurrentPrompt() {
if (mClingWindow != null) {
if (DEBUG) Slog.d(TAG, "confirmCurrentPrompt()");
mHandler.post(mConfirm);
@@ -191,16 +182,14 @@
}
}
- public WindowManager.LayoutParams getClingWindowLayoutParams() {
+ private WindowManager.LayoutParams getClingWindowLayoutParams() {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- 0
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- ,
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("ImmersiveModeConfirmation");
@@ -209,7 +198,7 @@
return lp;
}
- public FrameLayout.LayoutParams getBubbleLayoutParams() {
+ private FrameLayout.LayoutParams getBubbleLayoutParams() {
return new FrameLayout.LayoutParams(
mContext.getResources().getDimensionPixelSize(
R.dimen.immersive_mode_cling_width),
@@ -220,7 +209,7 @@
/**
* @return the window token that's used by all ImmersiveModeConfirmation windows.
*/
- public IBinder getWindowToken() {
+ IBinder getWindowToken() {
return mWindowToken;
}
@@ -272,7 +261,7 @@
}
};
- public ClingWindowView(Context context, Runnable confirm) {
+ ClingWindowView(Context context, Runnable confirm) {
super(context);
mConfirm = confirm;
setBackground(mColor);
@@ -295,7 +284,7 @@
mClingLayout = (ViewGroup)
View.inflate(getContext(), R.layout.immersive_mode_cling, null);
- final Button ok = (Button) mClingLayout.findViewById(R.id.ok);
+ final Button ok = mClingLayout.findViewById(R.id.ok);
ok.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -359,8 +348,7 @@
// we will be hiding the nav bar, so layout as if it's already hidden
mClingWindow.setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
// show the confirmation
WindowManager.LayoutParams lp = getClingWindowLayoutParams();
@@ -371,9 +359,9 @@
@Override
public void run() {
if (DEBUG) Slog.d(TAG, "mConfirm.run()");
- if (!mConfirmed) {
- mConfirmed = true;
- saveSetting();
+ if (!sConfirmed) {
+ sConfirmed = true;
+ saveSetting(mContext);
}
handleHide();
}
@@ -383,6 +371,10 @@
private static final int SHOW = 1;
private static final int HIDE = 2;
+ H(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
@@ -396,16 +388,13 @@
}
}
- private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
- @Override
- public void onVrStateChanged(boolean enabled) throws RemoteException {
- mVrModeEnabled = enabled;
- if (mVrModeEnabled) {
- mHandler.removeMessages(H.SHOW);
- mHandler.sendEmptyMessage(H.HIDE);
- }
+ void onVrStateChangedLw(boolean enabled) {
+ mVrModeEnabled = enabled;
+ if (mVrModeEnabled) {
+ mHandler.removeMessages(H.SHOW);
+ mHandler.sendEmptyMessage(H.HIDE);
}
- };
+ }
void onLockTaskModeChangedLw(int lockTaskState) {
mLockTaskState = lockTaskState;
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 61bc4e4..88b22cb 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -40,14 +40,12 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.WindowManager;
-import android.view.InputApplicationHandle;
import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
-import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
@@ -64,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();
@@ -75,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.
@@ -129,6 +128,7 @@
private boolean disposeInputConsumer(InputConsumerImpl consumer) {
if (consumer != null) {
consumer.disposeChannelsLw();
+ consumer.hide(mInputTransaction);
return true;
}
return false;
@@ -140,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);
}
}
@@ -180,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.
@@ -217,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
@@ -230,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;
}
}
@@ -264,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();
@@ -279,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.
@@ -365,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,19 +385,19 @@
mTmpRect.setEmpty();
mDisableWallpaperTouchEvents = false;
this.inDrag = inDrag;
- wallpaperController = mService.mRoot.mWallpaperController;
+ final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+ wallpaperController = dc.mWallpaperController;
- mService.mRoot.getDisplayContent(mDisplayId).forAllWindows(this,
+ 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);
}
@@ -423,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;
}
@@ -441,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;
}
}
@@ -493,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..e96f0b1
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.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.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.view.InsetsSource;
+
+import com.android.internal.util.function.TriConsumer;
+import com.android.server.policy.WindowManagerPolicy;
+
+/**
+ * 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 WindowState mWin;
+ private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
+
+ InsetsSourceProvider(InsetsSource source) {
+ mSource = source;
+ }
+
+ 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);
+
+ }
+}
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..1189ee6
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -0,0 +1,104 @@
+/*
+ * 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.util.ArrayMap;
+import android.view.InsetsState;
+
+import java.io.PrintWriter;
+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 ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>();
+
+ 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;
+ }
+
+ /**
+ * @return The provider of a specific type.
+ */
+ InsetsSourceProvider getSourceProvider(int type) {
+ return mControllers.computeIfAbsent(type,
+ key -> new InsetsSourceProvider(mState.getSource(key)));
+ }
+
+ /**
+ * 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();
+ }
+ }
+
+ 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/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 72d5143..bc6a690 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);
@@ -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..80dc245 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -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,11 +641,12 @@
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 ActivityRecord r = mSupervisor.mRootActivityContainer.topRunningActivity();
final TaskRecord task = (r != null) ? r.getTask() : null;
if (mLockTaskModeTasks.isEmpty() && task!= null
&& task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
@@ -657,7 +658,7 @@
}
if (taskChanged) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
diff --git a/services/core/java/com/android/server/policy/NavigationBarExperiments.java b/services/core/java/com/android/server/wm/NavigationBarExperiments.java
similarity index 96%
rename from services/core/java/com/android/server/policy/NavigationBarExperiments.java
rename to services/core/java/com/android/server/wm/NavigationBarExperiments.java
index 06772e3..b74fb45 100644
--- a/services/core/java/com/android/server/policy/NavigationBarExperiments.java
+++ b/services/core/java/com/android/server/wm/NavigationBarExperiments.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -25,9 +25,6 @@
import android.content.Context;
import android.graphics.Rect;
-import com.android.server.policy.WindowManagerPolicy.WindowState;
-import com.android.server.wm.WindowFrames;
-
/**
* This class acts as a proxy for Navigation Bar experiments enabled with custom overlays
* {@see OverlayManagerService}. By default with no overlays, this class will essentially do nothing
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/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index d21f67d..ba23258 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -514,8 +514,9 @@
*/
private void getInsetBounds(Rect outRect) {
synchronized (mService.mGlobalLock) {
- mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth,
- mDisplayInfo.logicalHeight, mDisplayInfo.displayCutout, mTmpInsets);
+ mDisplayContent.getDisplayPolicy().getStableInsetsLw(mDisplayInfo.rotation,
+ mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight,
+ mDisplayInfo.displayCutout, mTmpInsets);
outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y,
mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x,
mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y);
diff --git a/services/core/java/com/android/server/policy/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java
similarity index 92%
rename from services/core/java/com/android/server/policy/PolicyControl.java
rename to services/core/java/com/android/server/wm/PolicyControl.java
index 48e72bc..4c8ce9e 100644
--- a/services/core/java/com/android/server/policy/PolicyControl.java
+++ b/services/core/java/com/android/server/wm/PolicyControl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.wm;
import android.app.ActivityManager;
import android.content.Context;
@@ -26,8 +26,6 @@
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
-import com.android.server.policy.WindowManagerPolicy.WindowState;
-
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -49,9 +47,9 @@
* Separate multiple name-value pairs with ':'
* e.g. "immersive.status=apps:immersive.preconfirms=*"
*/
-public class PolicyControl {
- private static String TAG = "PolicyControl";
- private static boolean DEBUG = false;
+class PolicyControl {
+ private static final String TAG = "PolicyControl";
+ private static final boolean DEBUG = false;
private static final String NAME_IMMERSIVE_FULL = "immersive.full";
private static final String NAME_IMMERSIVE_STATUS = "immersive.status";
@@ -63,7 +61,7 @@
private static Filter sImmersiveStatusFilter;
private static Filter sImmersiveNavigationFilter;
- public static int getSystemUiVisibility(WindowState win, LayoutParams attrs) {
+ static int getSystemUiVisibility(WindowState win, LayoutParams attrs) {
attrs = attrs != null ? attrs : win.getAttrs();
int vis = win != null ? win.getSystemUiVisibility()
: (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility);
@@ -84,7 +82,7 @@
return vis;
}
- public static int getWindowFlags(WindowState win, LayoutParams attrs) {
+ static int getWindowFlags(WindowState win, LayoutParams attrs) {
attrs = attrs != null ? attrs : win.getAttrs();
int flags = attrs.flags;
if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
@@ -98,7 +96,7 @@
return flags;
}
- public static int adjustClearableFlags(WindowState win, int clearableFlags) {
+ static int adjustClearableFlags(WindowState win, int clearableFlags) {
final LayoutParams attrs = win != null ? win.getAttrs() : null;
if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
clearableFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -106,28 +104,32 @@
return clearableFlags;
}
- public static boolean disableImmersiveConfirmation(String pkg) {
+ static boolean disableImmersiveConfirmation(String pkg) {
return (sImmersivePreconfirmationsFilter != null
&& sImmersivePreconfirmationsFilter.matches(pkg))
|| ActivityManager.isRunningInTestHarness();
}
- public static void reloadFromSetting(Context context) {
+ static boolean reloadFromSetting(Context context) {
if (DEBUG) Slog.d(TAG, "reloadFromSetting()");
String value = null;
try {
value = Settings.Global.getStringForUser(context.getContentResolver(),
Settings.Global.POLICY_CONTROL,
UserHandle.USER_CURRENT);
- if (sSettingValue != null && sSettingValue.equals(value)) return;
+ if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) {
+ return false;
+ }
setFilters(value);
sSettingValue = value;
} catch (Throwable t) {
Slog.w(TAG, "Error loading policy control, value=" + value, t);
+ return false;
}
+ return true;
}
- public static void dump(String prefix, PrintWriter pw) {
+ static void dump(String prefix, PrintWriter pw) {
dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw);
dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw);
dump("sImmersivePreconfirmationsFilter", sImmersivePreconfirmationsFilter, prefix, pw);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 476c1f9..24c5228 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);
@@ -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/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
new file mode 100644
index 0000000..4dd48c4
--- /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.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;
+ }
+
+ /**
+ * 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.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) {
+ 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.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.
+ 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.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.
+ 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.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 = 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.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, 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.userId == userId)
+ || (resultTo != null && resultTo.userId == 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 2399716..80d1368 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -51,7 +51,6 @@
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
import android.annotation.CallSuper;
import android.annotation.NonNull;
@@ -83,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;
@@ -106,7 +109,6 @@
private boolean mSustainedPerformanceModeEnabled = false;
private boolean mSustainedPerformanceModeCurrent = false;
- boolean mWallpaperMayChange = false;
// During an orientation change, we track whether all windows have rendered
// at the new orientation, and this will be false from changing orientation until that occurs.
// For seamless rotation cases this always stays true, as the windows complete their orientation
@@ -114,8 +116,6 @@
boolean mOrientationChangeComplete = true;
boolean mWallpaperActionPending = false;
- final WallpaperController mWallpaperController;
-
private final Handler mHandler;
private String mCloseSystemDialogsReason;
@@ -124,13 +124,10 @@
// events.
int mTopFocusedDisplayId = INVALID_DISPLAY;
- // Only a seperate transaction until we seperate the apply surface changes
+ // Only a separate transaction until we separate the apply surface changes
// transaction from the global transaction.
private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction();
- private final Consumer<DisplayContent> mDisplayContentConfigChangesConsumer =
- mService.mPolicy::onConfigurationChanged;
-
private final Consumer<WindowState> mCloseSystemDialogsConsumer = w -> {
if (w.mHasSurface) {
try {
@@ -150,7 +147,13 @@
RootWindowContainer(WindowManagerService service) {
super(service);
mHandler = new MyHandler(service.mH.getLooper());
- mWallpaperController = new WallpaperController(mService);
+ }
+
+ void setRootActivityContainer(RootActivityContainer container) {
+ mRootActivityContainer = container;
+ if (container != null) {
+ container.registerConfigurationChangeListener(this);
+ }
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -197,9 +200,7 @@
mService.mH.sendMessage(msg);
}
});
- final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus;
- mService.mInputManager.setFocusedWindow(
- topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null);
+
return changed;
}
@@ -232,16 +233,17 @@
final DisplayContent existing = getDisplayContent(displayId);
if (existing != null) {
+ initializeDisplayOverrideConfiguration(controller, existing);
existing.setController(controller);
return existing;
}
- final DisplayContent dc =
- new DisplayContent(display, mService, mWallpaperController, controller);
+ final DisplayContent dc = new DisplayContent(display, mService, controller);
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
mService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
+ initializeDisplayOverrideConfiguration(controller, dc);
if (mService.mDisplayManagerInternal != null) {
mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
@@ -254,6 +256,19 @@
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) {
@@ -368,8 +383,6 @@
public void onConfigurationChanged(Configuration newParentConfig) {
prepareFreezingTaskBounds();
super.onConfigurationChanged(newParentConfig);
-
- forAllDisplays(mDisplayContentConfigChangesConsumer);
}
private void prepareFreezingTaskBounds() {
@@ -508,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();
}
}
@@ -579,14 +591,19 @@
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
if (recentsAnimationController != null) {
- recentsAnimationController.checkAnimationReady(mWallpaperController);
+ recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
- if (mWallpaperMayChange) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
- defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
- defaultDisplay.pendingLayoutChanges);
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final DisplayContent displayContent = mChildren.get(displayNdx);
+ if (displayContent.mWallpaperMayChange) {
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ if (DEBUG_LAYOUT_REPEATS) {
+ surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
+ displayContent.pendingLayoutChanges);
+ }
+ }
}
if (mService.mFocusMayChange) {
@@ -617,7 +634,6 @@
}
// Destroy the surface of any windows that are no longer visible.
- boolean wallpaperDestroyed = false;
i = mService.mDestroySurface.size();
if (i > 0) {
do {
@@ -629,7 +645,7 @@
displayContent.setInputMethodWindowLocked(null);
}
if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
- wallpaperDestroyed = true;
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
win.destroySurfaceUnchecked();
win.mWinAnimator.destroyPreservedSurfaceLocked();
@@ -643,11 +659,6 @@
displayContent.removeExistingTokensIfPossible();
}
- if (wallpaperDestroyed) {
- defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- defaultDisplay.setLayoutNeeded();
- }
-
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.pendingLayoutChanges != 0) {
@@ -905,10 +916,6 @@
mUpdateRotation = true;
doRequest = true;
}
- if ((bulkUpdateParams & SET_WALLPAPER_MAY_CHANGE) != 0) {
- mWallpaperMayChange = true;
- doRequest = true;
- }
if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
mOrientationChangeComplete = false;
} else {
@@ -1031,9 +1038,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);
}
}
@@ -1043,11 +1049,6 @@
}
@Override
- RootWindowContainerController getController() {
- return (RootWindowContainerController) super.getController();
- }
-
- @Override
void scheduleAnimation() {
mService.scheduleAnimationLocked();
}
@@ -1063,6 +1064,12 @@
}
}
+ void forAllDisplayPolicies(Consumer<DisplayPolicy> callback) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ callback.accept(mChildren.get(i).getDisplayPolicy());
+ }
+ }
+
/**
* Get current topmost focused IME window in system.
* Will look on all displays in current Z-order.
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/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/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 95ca0a6..05f556c 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -89,13 +89,16 @@
* Removing the transform and the result of the {@link WindowState} layout are both tied to the
* {@link WindowState} next frame, such that they apply at the same time the client draws the
* window in the new orientation.
+ *
+ * In the case of a rotation timeout, we want to remove the transform immediately and not defer
+ * it.
*/
- public void finish(WindowState win) {
+ public void finish(WindowState win, boolean timeout) {
mTransform.reset();
final Transaction t = win.getPendingTransaction();
t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
- if (win.mWinAnimator.mSurfaceController != null) {
+ if (win.mWinAnimator.mSurfaceController != null && !timeout) {
t.deferTransactionUntil(win.mSurfaceControl,
win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b411fad..37b5a7c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -29,7 +29,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.content.ClipData;
-import android.content.Context;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
@@ -42,7 +41,6 @@
import android.os.UserHandle;
import android.util.MergedConfiguration;
import android.util.Slog;
-import android.view.Display;
import android.view.DisplayCutout;
import android.view.IWindow;
import android.view.IWindowId;
@@ -52,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;
@@ -60,6 +59,7 @@
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;
+import java.util.function.BiConsumer;
/**
* This class represents an active client session. There is generally one
@@ -154,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
@@ -183,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);
@@ -191,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());
@@ -315,14 +319,19 @@
}
}
+ private void actionOnWallpaper(IBinder window,
+ BiConsumer<WallpaperController, WindowState> action) {
+ final WindowState windowState = mService.windowForClientLocked(this, window, true);
+ action.accept(windowState.getDisplayContent().mWallpaperController, windowState);
+ }
+
@Override
public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
synchronized (mService.mGlobalLock) {
long ident = Binder.clearCallingIdentity();
try {
- mService.mRoot.mWallpaperController.setWindowWallpaperPosition(
- mService.windowForClientLocked(this, window, true),
- x, y, xStep, yStep);
+ actionOnWallpaper(window, (wpController, windowState) ->
+ wpController.setWindowWallpaperPosition(windowState, x, y, xStep, yStep));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -332,7 +341,8 @@
@Override
public void wallpaperOffsetsComplete(IBinder window) {
synchronized (mService.mGlobalLock) {
- mService.mRoot.mWallpaperController.wallpaperOffsetsComplete(window);
+ actionOnWallpaper(window, (wpController, windowState) ->
+ wpController.wallpaperOffsetsComplete(window));
}
}
@@ -341,8 +351,8 @@
synchronized (mService.mGlobalLock) {
long ident = Binder.clearCallingIdentity();
try {
- mService.mRoot.mWallpaperController.setWindowWallpaperDisplayOffset(
- mService.windowForClientLocked(this, window, true), x, y);
+ actionOnWallpaper(window, (wpController, windowState) ->
+ wpController.setWindowWallpaperDisplayOffset(windowState, x, y));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -355,9 +365,9 @@
synchronized (mService.mGlobalLock) {
long ident = Binder.clearCallingIdentity();
try {
- return mService.mRoot.mWallpaperController.sendWindowWallpaperCommand(
- mService.windowForClientLocked(this, window, true),
- action, x, y, z, extras, sync);
+ final WindowState windowState = mService.windowForClientLocked(this, window, true);
+ return windowState.getDisplayContent().mWallpaperController
+ .sendWindowWallpaperCommand(windowState, action, x, y, z, extras, sync);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -367,7 +377,8 @@
@Override
public void wallpaperCommandComplete(IBinder window, Bundle result) {
synchronized (mService.mGlobalLock) {
- mService.mRoot.mWallpaperController.wallpaperCommandComplete(window);
+ actionOnWallpaper(window, (wpController, windowState) ->
+ wpController.wallpaperCommandComplete(window));
}
}
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index baeedbc..35264a2 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -261,11 +261,12 @@
final DisplayContent displayContent = stack.getDisplayContent();
final DisplayInfo di = displayContent.getDisplayInfo();
final DisplayCutout displayCutout = di.displayCutout;
+ final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
// Get the insets and display bounds
- mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ displayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
displayCutout, mTmpStableInsets);
- mService.mPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ displayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
displayCutout, mTmpNonDecorInsets);
mTmpDisplayBounds.set(0, 0, di.logicalWidth, di.logicalHeight);
diff --git a/services/core/java/com/android/server/wm/StatusBarController.java b/services/core/java/com/android/server/wm/StatusBarController.java
new file mode 100644
index 0000000..b4de75b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/StatusBarController.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.server.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
+
+import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+
+import android.app.StatusBarManager;
+import android.os.IBinder;
+import android.view.View;
+
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+/**
+ * Implements status bar specific behavior.
+ */
+public class StatusBarController extends BarController {
+
+ private final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
+
+ private Runnable mAppTransitionPending = () -> {
+ StatusBarManagerInternal statusBar = getStatusBarInternal();
+ if (statusBar != null && mWin != null) {
+ statusBar.appTransitionPending(mWin.getDisplayId());
+ }
+ };
+
+ private Runnable mAppTransitionCancelled = () -> {
+ StatusBarManagerInternal statusBar = getStatusBarInternal();
+ if (statusBar != null && mWin != null) {
+ statusBar.appTransitionCancelled(mWin.getDisplayId());
+ }
+ };
+
+ private Runnable mAppTransitionFinished = () -> {
+ StatusBarManagerInternal statusBar = getStatusBarInternal();
+ if (statusBar != null && mWin != null) {
+ statusBar.appTransitionFinished(mWin.getDisplayId());
+ }
+ };
+
+ @Override
+ public void onAppTransitionPendingLocked() {
+ mHandler.post(mAppTransitionPending);
+ }
+
+ @Override
+ public int onAppTransitionStartingLocked(int transit, IBinder openToken,
+ IBinder closeToken, long duration, long statusBarAnimationStartTime,
+ long statusBarAnimationDuration) {
+ mHandler.post(() -> {
+ StatusBarManagerInternal statusBar = getStatusBarInternal();
+ if (statusBar != null && mWin != null) {
+ statusBar.appTransitionStarting(mWin.getDisplayId(),
+ statusBarAnimationStartTime, statusBarAnimationDuration);
+ }
+ });
+ return 0;
+ }
+
+ @Override
+ public void onAppTransitionCancelledLocked(int transit) {
+ mHandler.post(mAppTransitionCancelled);
+ }
+
+ @Override
+ public void onAppTransitionFinishedLocked(IBinder token) {
+ mHandler.post(mAppTransitionFinished);
+ }
+ };
+
+ StatusBarController() {
+ super("StatusBar",
+ View.STATUS_BAR_TRANSIENT,
+ View.STATUS_BAR_UNHIDE,
+ View.STATUS_BAR_TRANSLUCENT,
+ StatusBarManager.WINDOW_STATUS_BAR,
+ FLAG_TRANSLUCENT_STATUS,
+ View.STATUS_BAR_TRANSPARENT);
+ }
+
+ void setTopAppHidesStatusBar(boolean hidesStatusBar) {
+ StatusBarManagerInternal statusBar = getStatusBarInternal();
+ if (statusBar != null) {
+ statusBar.setTopAppHidesStatusBar(hidesStatusBar);
+ }
+ }
+
+ @Override
+ protected boolean skipAnimation() {
+ return mWin.getAttrs().height == MATCH_PARENT;
+ }
+
+ AppTransitionListener getAppTransitionListener() {
+ return mAppTransitionListener;
+ }
+}
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/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
similarity index 93%
rename from services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
rename to services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
index d3cc8ef..bdb76c2 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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,11 +14,10 @@
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.wm;
import android.content.Context;
import android.os.Handler;
-import android.os.Looper;
import android.os.SystemClock;
import android.util.Slog;
import android.view.GestureDetector;
@@ -27,11 +26,11 @@
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.widget.OverScroller;
-/*
+/**
* Listens for system-wide input gestures, firing callbacks when detected.
* @hide
*/
-public class SystemGesturesPointerEventListener implements PointerEventListener {
+class SystemGesturesPointerEventListener implements PointerEventListener {
private static final String TAG = "SystemGestures";
private static final boolean DEBUG = false;
private static final long SWIPE_TIMEOUT_MS = 500;
@@ -46,6 +45,7 @@
private static final int SWIPE_FROM_LEFT = 4;
private final Context mContext;
+ private final Handler mHandler;
private final int mSwipeStartThreshold;
private final int mSwipeDistanceThreshold;
private final Callbacks mCallbacks;
@@ -55,7 +55,6 @@
private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];
private GestureDetector mGestureDetector;
- private OverScroller mOverscroller;
int screenHeight;
int screenWidth;
@@ -65,8 +64,9 @@
private boolean mMouseHoveringAtEdge;
private long mLastFlingTime;
- public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
+ SystemGesturesPointerEventListener(Context context, Handler handler, Callbacks callbacks) {
mContext = context;
+ mHandler = handler;
mCallbacks = checkNull("callbacks", callbacks);
mSwipeStartThreshold = checkNull("context", context).getResources()
.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
@@ -83,9 +83,7 @@
}
public void systemReady() {
- Handler h = new Handler(Looper.myLooper());
- mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h);
- mOverscroller = new OverScroller(mContext);
+ mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler);
}
@Override
@@ -163,14 +161,14 @@
private void captureDown(MotionEvent event, int pointerIndex) {
final int pointerId = event.getPointerId(pointerIndex);
final int i = findIndex(pointerId);
- if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
- " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
+ if (DEBUG) Slog.d(TAG, "pointer " + pointerId
+ + " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
if (i != UNTRACKED_POINTER) {
mDownX[i] = event.getX(pointerIndex);
mDownY[i] = event.getY(pointerIndex);
mDownTime[i] = event.getEventTime();
- if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
- " down x=" + mDownX[i] + " y=" + mDownY[i]);
+ if (DEBUG) Slog.d(TAG, "pointer " + pointerId
+ + " down x=" + mDownX[i] + " y=" + mDownY[i]);
}
}
@@ -242,6 +240,13 @@
}
private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
+
+ private OverScroller mOverscroller;
+
+ FlingGestureDetector() {
+ mOverscroller = new OverScroller(mContext);
+ }
+
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (!mOverscroller.isFinished()) {
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..6904ef5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -302,7 +302,6 @@
@Override
void onDisplayChanged(DisplayContent dc) {
- updateSurfaceSize(dc);
adjustBoundsForDisplayChangeIfNeeded(dc);
super.onDisplayChanged(dc);
}
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..5a70325 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -21,10 +21,14 @@
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.SurfaceControl;
import android.view.IWindow;
import com.android.internal.annotations.GuardedBy;
@@ -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);
@@ -157,6 +203,7 @@
mTaskPositioner.unregister();
mTaskPositioner = null;
}
+ mPositioningDisplay = null;
}
});
}
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index eec10ab..8a3dbad 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -472,8 +472,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 +544,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 +622,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 +655,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 +748,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.
@@ -982,7 +982,7 @@
@Override
protected void onParentChanged() {
super.onParentChanged();
- mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+ mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
}
// Close up recents linked list.
@@ -1143,7 +1143,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 +1182,7 @@
mActivities.add(newTop);
// Make sure window manager is aware of the position change.
- mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
+ mWindowContainerController.positionChildAtTop(newTop.mAppWindowToken);
updateEffectiveIntent();
setFrontOfTask();
@@ -1264,17 +1264,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();
}
/**
@@ -1683,9 +1681,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);
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 15de1ec..5deb4f1 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -248,7 +248,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);
@@ -521,7 +520,7 @@
// Snap the position to a target.
final int rotation = parentConfig.windowConfiguration.getRotation();
final int orientation = parentConfig.orientation;
- mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight,
+ mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight,
displayCutout, outBounds);
final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
mService.mContext.getResources(), displayWidth, displayHeight,
@@ -751,7 +750,6 @@
if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
return;
}
- transaction.setSize(mSurfaceControl, width, height);
transaction.setWindowCrop(mSurfaceControl, width, height);
mLastSurfaceSize.set(width, height);
}
@@ -867,7 +865,8 @@
// and its bounds can be adjusted after that. The bounds of all other stacks are
// adjusted to occupy whatever screen space the docked stack isn't occupying.
final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
- mService.mPolicy.getStableInsetsLw(parentConfig.windowConfiguration.getRotation(),
+ mDisplayContent.getDisplayPolicy().getStableInsetsLw(
+ parentConfig.windowConfiguration.getRotation(),
displayRect.width(), displayRect.height(), displayCutout, mTmpRect2);
final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
displayRect.width(),
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/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 29e1b20..15239c7 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -60,6 +60,7 @@
class WallpaperController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM;
private WindowManagerService mService;
+ private final DisplayContent mDisplayContent;
private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
@@ -187,8 +188,9 @@
return false;
};
- public WallpaperController(WindowManagerService service) {
+ WallpaperController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
+ mDisplayContent = displayContent;
}
WindowState getWallpaperTarget() {
@@ -397,11 +399,7 @@
}
private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
- final DisplayContent displayContent = changingTarget.getDisplayContent();
- if (displayContent == null) {
- return;
- }
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
@@ -464,15 +462,15 @@
}
}
- private void findWallpaperTarget(DisplayContent dc) {
+ private void findWallpaperTarget() {
mFindResults.reset();
- if (dc.isStackVisible(WINDOWING_MODE_FREEFORM)) {
+ if (mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM)) {
// In freeform mode we set the wallpaper as its own target, so we don't need an
// additional window to make it visible.
mFindResults.setUseTopWallpaperAsTarget(true);
}
- dc.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
+ mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) {
mFindResults.setWallpaperTarget(mFindResults.topWallpaper);
@@ -485,8 +483,7 @@
}
/** Updates the target wallpaper if needed and returns true if an update happened. */
- private void updateWallpaperWindowsTarget(DisplayContent dc,
- FindWallpaperTargetResult result) {
+ private void updateWallpaperWindowsTarget(FindWallpaperTargetResult result) {
WindowState wallpaperTarget = result.wallpaperTarget;
@@ -529,7 +526,7 @@
return;
}
- if (dc.getWindow(w -> w == prevWallpaperTarget) == null) {
+ if (mDisplayContent.getWindow(w -> w == prevWallpaperTarget) == null) {
return;
}
@@ -550,9 +547,9 @@
// is not. If they're both hidden, still use the new target.
mWallpaperTarget = prevWallpaperTarget;
} else if (newTargetHidden == oldTargetHidden
- && !dc.mOpeningApps.contains(wallpaperTarget.mAppToken)
- && (dc.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
- || dc.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
+ && !mDisplayContent.mOpeningApps.contains(wallpaperTarget.mAppToken)
+ && (mDisplayContent.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
+ || mDisplayContent.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
// If they're both hidden (or both not hidden), prefer the one that's currently in
// opening or closing app list, this allows transition selection logic to better
// determine the wallpaper status of opening/closing apps.
@@ -570,18 +567,21 @@
}
}
- void adjustWallpaperWindows(DisplayContent dc) {
- mService.mRoot.mWallpaperMayChange = false;
+ void adjustWallpaperWindows() {
+ mDisplayContent.mWallpaperMayChange = false;
// First find top-most window that has asked to be on top of the wallpaper;
// all wallpapers go behind it.
- findWallpaperTarget(dc);
- updateWallpaperWindowsTarget(dc, mFindResults);
+ findWallpaperTarget();
+ updateWallpaperWindowsTarget(mFindResults);
// The window is visible to the compositor...but is it visible to the user?
// That is what the wallpaper cares about.
final boolean visible = mWallpaperTarget != null && isWallpaperVisible(mWallpaperTarget);
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);
+ if (DEBUG_WALLPAPER) {
+ Slog.v(TAG, "Wallpaper visibility: " + visible + " at display "
+ + mDisplayContent.getDisplayId());
+ }
if (visible) {
if (mWallpaperTarget.mWallpaperX >= 0) {
@@ -637,9 +637,11 @@
}
if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
- mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(WALLPAPER_DRAW_PENDING_TIMEOUT,
- WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+ mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this);
+ mService.mH.sendMessageDelayed(
+ mService.mH.obtainMessage(WALLPAPER_DRAW_PENDING_TIMEOUT, this),
+ WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+
}
if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper should be visible but has not been drawn yet. " +
@@ -649,7 +651,7 @@
}
if (wallpaperReady) {
mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
- mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
+ mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this);
}
return transitionReady;
@@ -659,10 +661,9 @@
* Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
* the opening apps should be a wallpaper target.
*/
- void adjustWallpaperWindowsForAppTransitionIfNeeded(DisplayContent dc,
- ArraySet<AppWindowToken> openingApps) {
+ void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<AppWindowToken> openingApps) {
boolean adjust = false;
- if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
adjust = true;
} else {
for (int i = openingApps.size() - 1; i >= 0; --i) {
@@ -675,7 +676,7 @@
}
if (adjust) {
- adjustWallpaperWindows(dc);
+ adjustWallpaperWindows();
}
}
@@ -740,6 +741,7 @@
}
void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId());
pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
if (mPrevWallpaperTarget != null) {
pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget);
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 46bb981..b8a0739 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -16,7 +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;
@@ -259,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);
}
}
}
@@ -270,9 +268,6 @@
if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
builder.append(" UPDATE_ROTATION");
}
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE) != 0) {
- builder.append(" WALLPAPER_MAY_CHANGE");
- }
if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
builder.append(" ORIENTATION_CHANGE_COMPLETE");
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 266006d..7e4c629 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -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);
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/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index b096bf2..e83b863 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -392,11 +392,6 @@
public abstract boolean isStackVisible(int windowingMode);
/**
- * @return True if and only if the docked divider is currently in resize mode.
- */
- public abstract boolean isDockedDividerResizing();
-
- /**
* Requests the window manager to resend the windows for accessibility.
*/
public abstract void computeWindowsForAccessibility();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c47b22f..4085f3d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -171,6 +171,8 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -216,6 +218,7 @@
import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowContentFrameStats;
+import android.view.InsetsState;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.RemoveContentMode;
@@ -231,6 +234,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
@@ -373,6 +377,18 @@
boolean mKeyguardOrAodShowingOnDefaultDisplay;
// VR Vr2d Display Id.
int mVr2dDisplayId = INVALID_DISPLAY;
+ boolean mVrModeEnabled = false;
+
+ private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+ @Override
+ public void onVrStateChanged(boolean enabled) {
+ synchronized (mGlobalLock) {
+ mVrModeEnabled = enabled;
+ mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
+ DisplayPolicy::onVrStateChangedLw, PooledLambda.__(), enabled));
+ }
+ }
+ };
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -528,6 +544,7 @@
boolean mForceDisplayEnabled = false;
boolean mShowingBootMessages = false;
boolean mBootAnimationStopped = false;
+ boolean mSystemReady = false;
// Following variables are for debugging screen wakelock only.
WindowState mLastWakeLockHoldingWindow = null;
@@ -579,14 +596,10 @@
final WallpaperVisibilityListeners mWallpaperVisibilityListeners =
new WallpaperVisibilityListeners();
- int mSystemDecorLayer = 0;
- final Rect mScreenRect = new Rect();
-
boolean mDisplayFrozen = false;
long mDisplayFreezeTime = 0;
int mLastDisplayFreezeDuration = 0;
Object mLastFinishedFreezeSource = null;
- boolean mWaitingForConfig = false;
boolean mSwitchingUser = false;
final static int WINDOWS_FREEZING_SCREENS_NONE = 0;
@@ -597,11 +610,6 @@
boolean mClientFreezingScreen = false;
int mAppsFreezingScreen = 0;
- // Last systemUiVisibility we received from status bar.
- int mLastStatusBarVisibility = 0;
- // Last systemUiVisibility we dispatched to windows.
- int mLastDispatchedSystemUiVisibility = 0;
-
// State while inside of layoutAndPlaceSurfacesLocked().
boolean mFocusMayChange;
@@ -649,6 +657,10 @@
Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE);
private final Uri mAnimationDurationScaleUri =
Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE);
+ private final Uri mImmersiveModeConfirmationsUri =
+ Settings.Secure.getUriFor(Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS);
+ private final Uri mPolicyControlUri =
+ Settings.Global.getUriFor(Settings.Global.POLICY_CONTROL);
public SettingsObserver() {
super(new Handler());
@@ -661,6 +673,10 @@
UserHandle.USER_ALL);
resolver.registerContentObserver(mAnimationDurationScaleUri, false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(mImmersiveModeConfirmationsUri, false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(mPolicyControlUri, false, this,
+ UserHandle.USER_ALL);
}
@Override
@@ -669,23 +685,40 @@
return;
}
+ if (mImmersiveModeConfirmationsUri.equals(uri) || mPolicyControlUri.equals(uri)) {
+ updateSystemUiSettings();
+ return;
+ }
+
if (mDisplayInversionEnabledUri.equals(uri)) {
updateCircularDisplayMaskIfNeeded();
+ return;
+ }
+
+ @UpdateAnimationScaleMode
+ final int mode;
+ if (mWindowAnimationScaleUri.equals(uri)) {
+ mode = WINDOW_ANIMATION_SCALE;
+ } else if (mTransitionAnimationScaleUri.equals(uri)) {
+ mode = TRANSITION_ANIMATION_SCALE;
+ } else if (mAnimationDurationScaleUri.equals(uri)) {
+ mode = ANIMATION_DURATION_SCALE;
} else {
- @UpdateAnimationScaleMode
- final int mode;
- if (mWindowAnimationScaleUri.equals(uri)) {
- mode = WINDOW_ANIMATION_SCALE;
- } else if (mTransitionAnimationScaleUri.equals(uri)) {
- mode = TRANSITION_ANIMATION_SCALE;
- } else if (mAnimationDurationScaleUri.equals(uri)) {
- mode = ANIMATION_DURATION_SCALE;
- } else {
- // Ignoring unrecognized content changes
- return;
- }
- Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0);
- mH.sendMessage(m);
+ // Ignoring unrecognized content changes
+ return;
+ }
+ Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0);
+ mH.sendMessage(m);
+ }
+
+ void updateSystemUiSettings() {
+ boolean changed;
+ synchronized (mWindowMap) {
+ changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext)
+ || PolicyControl.reloadFromSetting(mContext);
+ }
+ if (changed) {
+ updateRotation(false /* alwaysSendConfiguration */, false /* forceRelayout */);
}
}
}
@@ -1079,7 +1112,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) {
@@ -1286,10 +1320,11 @@
final boolean hasStatusBarServicePermission =
mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
== PackageManager.PERMISSION_GRANTED;
- mPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);
+ final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
+ displayPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
- res = mPolicy.prepareAddWindowLw(win, attrs);
+ res = displayPolicy.prepareAddWindowLw(win, attrs);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
@@ -1422,11 +1457,11 @@
taskBounds = null;
floatingStack = false;
}
- if (mPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack,
- outFrame, outContentInsets, outStableInsets, outOutsets,
- outDisplayCutout)) {
+ if (displayPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack,
+ 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;
@@ -1824,7 +1859,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 =
@@ -1837,11 +1872,13 @@
long origId = Binder.clearCallingIdentity();
final int displayId;
synchronized (mGlobalLock) {
- WindowState win = windowForClientLocked(session, client, false);
+ final WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
displayId = win.getDisplayId();
+ final DisplayContent displayContent = win.getDisplayContent();
+ final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
WindowStateAnimator winAnimator = win.mWinAnimator;
if (viewVisibility != View.GONE) {
@@ -1850,14 +1887,15 @@
win.setFrameNumber(frameNumber);
- if (!mWaitingForConfig) {
- win.finishSeamlessRotation();
+ final DisplayContent dc = win.getDisplayContent();
+ if (!dc.mWaitingForConfig) {
+ win.finishSeamlessRotation(false /* timeout */);
}
int attrChanges = 0;
int flagChanges = 0;
if (attrs != null) {
- mPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);
+ displayPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);
// if they don't have the permission, mask out the status bar bits
if (seq == win.mSeq) {
int systemUiVisibility = attrs.systemUiVisibility
@@ -1996,7 +2034,7 @@
try {
result = createSurfaceControl(outSurface, result, win, winAnimator);
} catch (Exception e) {
- win.getDisplayContent().getInputMonitor().updateInputWindowsLw(true /*force*/);
+ displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
Slog.w(TAG_WM, "Exception thrown when creating surface for client "
+ client + " (" + win.mAttrs.getTitle() + ")",
@@ -2007,7 +2045,6 @@
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = true;
}
- final DisplayContent displayContent = win.getDisplayContent();
if (win.mAttrs.type == TYPE_INPUT_METHOD
&& displayContent.mInputMethodWindow == null) {
displayContent.setInputMethodWindowLocked(win);
@@ -2052,35 +2089,34 @@
// updateFocusedWindowLocked() already assigned layers so we only need to
// reassign them at this point if the IM window state gets shuffled
boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
- final DisplayContent dc = win.getDisplayContent();
if (imMayMove) {
- dc.computeImeTarget(true /* updateImeTarget */);
+ displayContent.computeImeTarget(true /* updateImeTarget */);
if (toBeDisplayed) {
// Little hack here -- we -should- be able to rely on the function to return
// true if the IME has moved and needs its layer recomputed. However, if the IME
// was hidden and isn't actually moved in the list, its layer may be out of data
// so we make sure to recompute it.
- dc.assignWindowLayers(false /* setLayoutNeeded */);
+ displayContent.assignWindowLayers(false /* setLayoutNeeded */);
}
}
if (wallpaperMayMove) {
- win.getDisplayContent().pendingLayoutChanges |=
+ displayContent.pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
if (win.mAppToken != null) {
- dc.mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
+ displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"relayoutWindow: updateOrientationFromAppTokens");
- configChanged = dc.updateOrientationFromAppTokens();
+ configChanged = displayContent.updateOrientationFromAppTokens();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (toBeDisplayed && win.mIsWallpaper) {
- DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo();
- dc.mWallpaperController.updateWallpaperOffset(
+ DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ displayContent.mWallpaperController.updateWallpaperOffset(
win, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
}
if (win.mAppToken != null) {
@@ -2090,7 +2126,7 @@
winAnimator.mReportSurfaceResized = false;
result |= WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED;
}
- if (mPolicy.isNavBarForcedShownLw(win)) {
+ if (displayPolicy.isNavBarForcedShownLw(win)) {
result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR;
}
if (!win.isGoneForLayoutLw()) {
@@ -2124,6 +2160,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
@@ -2286,7 +2323,7 @@
}
synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
if (dc == null) {
Slog.w(TAG_WM, "addWindowToken: Attempted to add token: " + binder
+ " for non-exiting displayId=" + displayId);
@@ -2405,10 +2442,10 @@
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
displayContent.computeScreenConfiguration(mTempConfiguration);
if (currentConfig.diff(mTempConfiguration) != 0) {
- mWaitingForConfig = true;
+ displayContent.mWaitingForConfig = true;
displayContent.setLayoutNeeded();
int anim[] = new int[2];
- mPolicy.selectRotationAnimationLw(anim);
+ displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
startFreezingDisplayLocked(anim[0], anim[1], displayContent);
config = new Configuration(mTempConfiguration);
@@ -2418,9 +2455,10 @@
return config;
}
- void setNewDisplayOverrideConfiguration(Configuration overrideConfig, DisplayContent dc) {
- if (mWaitingForConfig) {
- mWaitingForConfig = false;
+ void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
+ @NonNull DisplayContent dc) {
+ if (dc.mWaitingForConfig) {
+ dc.mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
@@ -2441,26 +2479,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);
}
}
@@ -2603,7 +2651,9 @@
}
}
- @Override
+ /**
+ * Notifies window manager that {@link DisplayPolicy#isShowingDreamLw} has changed.
+ */
public void notifyShowingDreamChanged() {
// TODO(multi-display): support show dream in multi-display.
notifyKeyguardFlagsChanged(null /* callback */, DEFAULT_DISPLAY);
@@ -2634,6 +2684,21 @@
mH.sendEmptyMessage(H.RECOMPUTE_FOCUS);
}
+ @Override
+ public void onPowerKeyDown(boolean isScreenOn) {
+ mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
+ DisplayPolicy::onPowerKeyDown, PooledLambda.__(), isScreenOn));
+ }
+
+ @Override
+ public void onUserSwitched() {
+ mSettingsObserver.updateSystemUiSettings();
+ synchronized (mWindowMap) {
+ // force a re-application of focused window sysui visibility on each display.
+ mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemUiVisibilityLw);
+ }
+ }
+
/**
* Starts deferring layout passes. Useful when doing multiple changes but to optimize
* performance, only one layout pass should be done. This can be called multiple times, and
@@ -2839,7 +2904,8 @@
public boolean isShowingDream() {
synchronized (mGlobalLock) {
- return mPolicy.isShowingDreamLw();
+ // TODO: fix this when dream can be shown on non-default display.
+ return getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw();
}
}
@@ -3462,7 +3528,9 @@
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
synchronized (mGlobalLock) {
- return mRoot.mWallpaperController.screenshotWallpaperLocked();
+ // TODO(b/115486823) Screenshot at secondary displays if needed.
+ final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY);
+ return dc.mWallpaperController.screenshotWallpaperLocked();
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3523,6 +3591,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);
@@ -3670,13 +3749,6 @@
}
@Override
- public WindowManagerPolicy.DisplayContentInfo getDefaultDisplayContentInfo() {
- synchronized (mGlobalLock) {
- return getDefaultDisplayContentLocked();
- }
- }
-
- @Override
public int getDefaultDisplayRotation() {
synchronized (mGlobalLock) {
return getDefaultDisplayContentLocked().getRotation();
@@ -4135,13 +4207,11 @@
// placement to unfreeze the display since we froze it when the rotation was updated
// in DisplayContent#updateRotationUnchecked.
synchronized (mGlobalLock) {
- if (mWaitingForConfig) {
- mWaitingForConfig = false;
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc != null && dc.mWaitingForConfig) {
+ dc.mWaitingForConfig = false;
mLastFinishedFreezeSource = "config-unchanged";
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc != null) {
- dc.setLayoutNeeded();
- }
+ dc.setLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -4289,10 +4359,29 @@
}
public void systemReady() {
+ mSystemReady = true;
mPolicy.systemReady();
+ mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady);
mTaskSnapshotController.systemReady();
mHasWideColorGamutSupport = queryWideColorGamutSupport();
mHasHdrSupport = queryHdrSupport();
+ UiThread.getHandler().post(mSettingsObserver::updateSystemUiSettings);
+ IVrManager vrManager = IVrManager.Stub.asInterface(
+ ServiceManager.getService(Context.VR_SERVICE));
+ if (vrManager != null) {
+ try {
+ final boolean vrModeEnabled = vrManager.getVrModeState();
+ synchronized (mGlobalLock) {
+ vrManager.registerListener(mVrStateCallbacks);
+ if (vrModeEnabled) {
+ mVrModeEnabled = vrModeEnabled;
+ mVrStateCallbacks.onVrStateChanged(vrModeEnabled);
+ }
+ }
+ } catch (RemoteException e) {
+ // Ignore, we cannot do anything if we failed to register VR mode listener
+ }
+ }
}
private static boolean queryWideColorGamutSupport() {
@@ -4694,7 +4783,10 @@
break;
case WALLPAPER_DRAW_PENDING_TIMEOUT: {
synchronized (mGlobalLock) {
- if (mRoot.mWallpaperController.processWallpaperDrawPendingTimeout()) {
+ final WallpaperController wallpaperController =
+ (WallpaperController) msg.obj;
+ if (wallpaperController != null
+ && wallpaperController.processWallpaperDrawPendingTimeout()) {
mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -5068,7 +5160,7 @@
configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
if (configChanged) {
- mWaitingForConfig = true;
+ displayContent.mWaitingForConfig = true;
startFreezingDisplayLocked(0 /* exitAnim */,
0 /* enterAnim */, displayContent);
displayContent.sendNewConfiguration();
@@ -5316,7 +5408,7 @@
displayContent.updateDisplayInfo();
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
- displayContent.getDisplayRotation().isDefaultOrientationForced(), isSecure,
+ displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
this);
mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
screenRotationAnimation);
@@ -5328,23 +5420,24 @@
return;
}
- final DisplayContent dc = mRoot.getDisplayContent(mFrozenDisplayId);
- if (mWaitingForConfig || mAppsFreezingScreen > 0
+ final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
+ final boolean waitingForConfig = displayContent != null && displayContent.mWaitingForConfig;
+ final int numOpeningApps = displayContent != null ? displayContent.mOpeningApps.size() : 0;
+ if (waitingForConfig || mAppsFreezingScreen > 0
|| mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
- || mClientFreezingScreen || (dc != null && !dc.mOpeningApps.isEmpty())) {
+ || mClientFreezingScreen || numOpeningApps > 0) {
if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
- "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig
+ "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + waitingForConfig
+ ", mAppsFreezingScreen=" + mAppsFreezingScreen
+ ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
+ ", mClientFreezingScreen=" + mClientFreezingScreen
- + ", mOpeningApps.size()=" + (dc != null ? dc.mOpeningApps.size() : 0));
+ + ", mOpeningApps.size()=" + numOpeningApps);
return;
}
if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
"stopFreezingDisplayLocked: Unfreezing now");
- final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
// We must make a local copy of the displayId as it can be potentially overwritten later on
// in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
@@ -5377,7 +5470,8 @@
if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation");
DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Get rotation animation again, with new top window
- if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
+ if (!displayContent.getDisplayPolicy()
+ .validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
mExitAnimId = mEnterAnimId = 0;
}
if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
@@ -5517,7 +5611,7 @@
}
@Override
- public void statusBarVisibilityChanged(int visibility) {
+ public void statusBarVisibilityChanged(int displayId, int visibility) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller does not hold permission "
@@ -5525,9 +5619,12 @@
}
synchronized (mGlobalLock) {
- mLastStatusBarVisibility = visibility;
- visibility = mPolicy.adjustSystemUiVisibilityLw(visibility);
- updateStatusBarVisibilityLocked(visibility);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.statusBarVisibilityChanged(visibility);
+ } else {
+ Slog.w(TAG, "statusBarVisibilityChanged with invalid displayId=" + displayId);
+ }
}
}
@@ -5543,49 +5640,36 @@
}
}
- // TODO(multidisplay): StatusBar on multiple screens?
- private boolean updateStatusBarVisibilityLocked(int visibility) {
- if (mLastDispatchedSystemUiVisibility == visibility) {
- return false;
- }
- final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility)
- // We are only interested in differences of one of the
- // clearable flags...
- & View.SYSTEM_UI_CLEARABLE_FLAGS
- // ...if it has actually been cleared.
- & ~visibility;
-
- mLastDispatchedSystemUiVisibility = visibility;
- mInputManager.setSystemUiVisibility(visibility);
- getDefaultDisplayContentLocked().updateSystemUiVisibility(visibility, globalDiff);
- return true;
- }
-
+ // TODO: Make the callers use getNavBarPosition(int) only.
+ /**
+ * Used by SystemUI and shared SystemUI libraries.
+ * @see DisplayPolicy#getNavBarPosition()
+ */
@Override
- public void reevaluateStatusBarVisibility() {
- synchronized (mGlobalLock) {
- int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
- if (updateStatusBarVisibilityLocked(visibility)) {
- mWindowPlacerLocked.requestTraversal();
- }
- }
+ @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 WindowManagerPolicy#getNavBarPosition()
+ * @see DisplayPolicy#getNavBarPosition()
*/
- @Override
@WindowManagerPolicy.NavigationBarPosition
- public int getNavBarPosition() {
+ public int getNavBarPosition(int displayId) {
synchronized (mGlobalLock) {
// Perform layout if it was scheduled before to make sure that we get correct nav bar
// position when doing rotations.
- final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
- defaultDisplayContent.performLayout(false /* initial */,
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ Slog.w(TAG, "getNavBarPosition with invalid displayId=" + displayId
+ + " callers=" + Debug.getCallers(3));
+ return -1;
+ }
+ displayContent.performLayout(false /* initial */,
false /* updateInputWindows */);
- return mPolicy.getNavBarPosition();
+ return displayContent.getDisplayPolicy().getNavBarPosition();
}
}
@@ -5648,8 +5732,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
@@ -5771,11 +5861,6 @@
}
}
- @Override
- public int getDockedDividerInsetsLw() {
- return getDefaultDisplayContentLocked().getDockedDividerController().getContentInsets();
- }
-
private void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) {
pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
mPolicy.dump(" ", pw, args);
@@ -5964,18 +6049,11 @@
mTaskSnapshotController.dump(pw, " ");
if (dumpAll) {
- pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer);
- pw.print(" mScreenRect="); pw.println(mScreenRect.toShortString());
- if (mLastStatusBarVisibility != 0) {
- pw.print(" mLastStatusBarVisibility=0x");
- pw.println(Integer.toHexString(mLastStatusBarVisibility));
- }
final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
if (imeWindow != null) {
pw.print(" mInputMethodWindow="); pw.println(imeWindow);
}
mWindowPlacerLocked.dump(pw, " ");
- mRoot.mWallpaperController.dump(pw, " ");
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
@@ -5986,7 +6064,6 @@
pw.print(" windows="); pw.print(mWindowsFreezingScreen);
pw.print(" client="); pw.print(mClientFreezingScreen);
pw.print(" apps="); pw.print(mAppsFreezingScreen);
- pw.print(" waitingForConfig="); pw.println(mWaitingForConfig);
final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
pw.print(" mRotation="); pw.print(defaultDisplayContent.getRotation());
pw.print(" mAltOrientation=");
@@ -5995,6 +6072,9 @@
pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
pw.print(" mLastOrientation=");
pw.println(defaultDisplayContent.getLastOrientation());
+ pw.print(" waitingForConfig=");
+ pw.println(defaultDisplayContent.mWaitingForConfig);
+
pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled);
pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
@@ -6003,6 +6083,7 @@
pw.print(" mRecentsAnimationController="); pw.println(mRecentsAnimationController);
mRecentsAnimationController.dump(pw, " ");
}
+ PolicyControl.dump(" ", pw);
}
}
@@ -6266,7 +6347,7 @@
public void onOverlayChanged() {
synchronized (mGlobalLock) {
mRoot.forAllDisplays(displayContent -> {
- mPolicy.onOverlayChangedLw(displayContent);
+ displayContent.getDisplayPolicy().onOverlayChangedLw();
displayContent.updateDisplayInfo();
});
requestTraversal();
@@ -6496,7 +6577,7 @@
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc != null) {
final DisplayInfo di = dc.getDisplayInfo();
- mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
di.displayCutout, outInsets);
}
}
@@ -7098,13 +7179,6 @@
}
@Override
- public boolean isDockedDividerResizing() {
- synchronized (mGlobalLock) {
- return getDefaultDisplayContentLocked().getDockedDividerController().isResizing();
- }
- }
-
- @Override
public void computeWindowsForAccessibility() {
final AccessibilityController accessibilityController;
synchronized (mGlobalLock) {
@@ -7363,9 +7437,11 @@
* {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
* {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
*/
- public void onLockTaskStateChanged(int lockTaskState) {
+ void onLockTaskStateChanged(int lockTaskState) {
+ // TODO: pass in displayId to determine which display the lock task state changed
synchronized (mGlobalLock) {
- mPolicy.onLockTaskStateChangedLw(lockTaskState);
+ mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
+ DisplayPolicy::onLockTaskStateChangedLw, PooledLambda.__(), lockTaskState));
}
}
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..578af2e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -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 a117cf3..cfd1f86 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;
@@ -179,6 +180,8 @@
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
+import android.view.InputWindowHandle;
+import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
@@ -191,8 +194,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
-import android.view.InputWindowHandle;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.policy.WindowManagerPolicy.DisplayContentInfo;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import com.android.server.wm.utils.InsetUtils;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -577,10 +580,12 @@
*/
private boolean mIsDimming = false;
+ private @Nullable InsetsSourceProvider mInsetProvider;
+
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
- void seamlesslyRotateIfAllowed(Transaction transaction, int oldRotation, int rotation,
- boolean requested) {
+ void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
+ @Rotation int rotation, boolean requested) {
// Invisible windows and the wallpaper do not participate in the seamless rotation animation
if (!isVisibleNow() || mIsWallpaper) {
return;
@@ -597,9 +602,9 @@
}
}
- void finishSeamlessRotation() {
+ void finishSeamlessRotation(boolean timeout) {
if (mPendingSeamlessRotate != null) {
- mPendingSeamlessRotate.finish(this);
+ mPendingSeamlessRotate.finish(this, timeout);
mFinishSeamlessRotateFrameNumber = getFrameNumber();
mPendingSeamlessRotate = null;
mService.markForSeamlessRotation(this, false);
@@ -1260,7 +1265,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) {
@@ -1800,7 +1804,7 @@
// also been registered in display.
dc.mTapExcludeProvidingWindows.remove(this);
}
- mPolicy.removeWindowLw(this);
+ dc.getDisplayPolicy().removeWindowLw(this);
disposeInputChannel();
@@ -2036,7 +2040,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();
@@ -2067,7 +2071,7 @@
mClientChannel.dispose();
mClientChannel = null;
}
- mInputWindowHandle.inputChannel = null;
+ mInputWindowHandle.token = null;
}
/** Returns true if the replacement window was removed. */
@@ -2166,11 +2170,15 @@
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);
}
+
+ // The area containing the shadows is not touchable.
+ region.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top);
+
return flags;
}
@@ -2391,11 +2399,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();
}
@@ -2802,25 +2810,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) {
@@ -2944,6 +2959,18 @@
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);
+ }
+ }
+
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
@@ -2982,7 +3009,7 @@
mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
- mPolicy.isNavBarForcedShownLw(this), displayId,
+ getDisplayContent().getDisplayPolicy().isNavBarForcedShownLw(this), displayId,
new DisplayCutout.ParcelableWrapper(displayCutout));
mDragResizingChangeReported = true;
}
@@ -4765,6 +4792,14 @@
mWindowFrames.setContentChanged(false);
}
+ void setInsetProvider(InsetsSourceProvider insetProvider) {
+ mInsetProvider = insetProvider;
+ }
+
+ InsetsSourceProvider getInsetProvider() {
+ return mInsetProvider;
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 838d2a1..78a3fe5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -245,7 +245,7 @@
mSession = win.mSession;
mAttrType = win.mAttrs.type;
mIsWallpaper = win.mIsWallpaper;
- mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
+ mWallpaperControllerLocked = win.getDisplayContent().mWallpaperController;
}
void cancelExitAnimationForNextAnimationLocked() {
@@ -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;
@@ -1343,7 +1343,7 @@
// is running.
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");
if (mWin.mToken.okToAnimate()) {
- int anim = mPolicy.selectAnimationLw(mWin, transit);
+ int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimationLw(mWin, transit);
int attr = -1;
Animation a = null;
if (anim != 0) {
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/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7d25b8c..2ee58fe 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -38,7 +38,6 @@
class WindowSurfacePlacer {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfacePlacer" : TAG_WM;
private final WindowManagerService mService;
- private final WallpaperController mWallpaperControllerLocked;
private boolean mInLayout = false;
@@ -46,7 +45,6 @@
private int mLayoutRepeatCount;
static final int SET_UPDATE_ROTATION = 1 << 0;
- static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 2;
static final int SET_WALLPAPER_ACTION_PENDING = 1 << 3;
@@ -59,7 +57,6 @@
public WindowSurfacePlacer(WindowManagerService service) {
mService = service;
- mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
mPerformSurfacePlacement = () -> {
synchronized (mService.mGlobalLock) {
performSurfacePlacement();
@@ -116,7 +113,9 @@
return;
}
- if (mService.mWaitingForConfig) {
+ // TODO(multi-display):
+ final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
+ if (defaultDisplay.mWaitingForConfig) {
// Our configuration has changed (most likely rotation), but we
// don't yet have the complete configuration to report to
// applications. Don't do any window layout until we have it.
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 0cf79b6..d8242f8 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -265,7 +265,6 @@
// to another display before the window behind
// it is ready.
- updateSurfaceSize(dc);
super.onDisplayChanged(dc);
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 04a526f..bf83ac13 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -83,6 +83,7 @@
"libui",
"libinput",
"libinputflinger",
+ "libinputflinger_base",
"libinputservice",
"libschedulerservicehidl",
"libsensorservice",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e2db807..fcd9335 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -86,6 +86,7 @@
jmethodID notifySwitch;
jmethodID notifyInputChannelBroken;
jmethodID notifyANR;
+ jmethodID notifyFocusChanged;
jmethodID filterInputEvent;
jmethodID interceptKeyBeforeQueueing;
jmethodID interceptMotionBeforeQueueingNonInteractive;
@@ -147,15 +148,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,
@@ -249,6 +241,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);
@@ -341,8 +334,9 @@
}
mInteractive = true;
- sp<EventHub> eventHub = new EventHub();
- mInputManager = new InputManager(eventHub, this, this);
+ mInputManager = new InputManager(this, this);
+ defaultServiceManager()->addService(String16("inputflinger"),
+ mInputManager, false);
}
NativeInputManager::~NativeInputManager() {
@@ -657,13 +651,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
@@ -672,7 +664,6 @@
}
env->DeleteLocalRef(reasonObj);
- env->DeleteLocalRef(inputApplicationHandleObj);
return newTimeout;
}
@@ -692,6 +683,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();
@@ -1716,10 +1723,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");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
new file mode 100644
index 0000000..05912a5
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
@@ -0,0 +1,268 @@
+/*
+ * 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.devicepolicy;
+
+import android.app.admin.DevicePolicyManager.InstallUpdateCallback;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.UpdateEngine;
+import android.os.UpdateEngineCallback;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+/**
+ * Used for installing an update on <a href="https://source.android.com/devices/tech/ota/ab">AB
+ * devices.</a>
+ * <p>This logic is specific to GOTA and should be modified by OEMs using a different AB update
+ * system.</p>
+ */
+class AbUpdateInstaller extends UpdateInstaller {
+ private static final String PAYLOAD_BIN = "payload.bin";
+ private static final String PAYLOAD_PROPERTIES_TXT = "payload_properties.txt";
+ //https://en.wikipedia.org/wiki/Zip_(file_format)#Local_file_header
+ private static final int OFFSET_TO_FILE_NAME = 30;
+ // kDownloadStateInitializationError constant from system/update_engine/common/error_code.h.
+ private static final int DOWNLOAD_STATE_INITIALIZATION_ERROR = 20;
+ private long mSizeForUpdate;
+ private long mOffsetForUpdate;
+ private List<String> mProperties;
+ private Enumeration<? extends ZipEntry> mEntries;
+ private ZipFile mPackedUpdateFile;
+ private static final Map<Integer, Integer> errorCodesMap = buildErrorCodesMap();
+ private static final Map<Integer, String> errorStringsMap = buildErrorStringsMap();
+ public static final String UNKNOWN_ERROR = "Unknown error with error code = ";
+ private boolean mUpdateInstalled;
+
+ private static Map<Integer, Integer> buildErrorCodesMap() {
+ Map<Integer, Integer> map = new HashMap<>();
+ map.put(UpdateEngine.ErrorCodeConstants.ERROR, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+ map.put(
+ DOWNLOAD_STATE_INITIALIZATION_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+
+ // Error constants corresponding to errors related to bad update file.
+ map.put(
+ UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+
+ // Error constants corresponding to errors related to devices bad state.
+ map.put(
+ UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+
+ return map;
+ }
+
+ private static Map<Integer, String> buildErrorStringsMap() {
+ Map<Integer, String> map = new HashMap<>();
+ map.put(UpdateEngine.ErrorCodeConstants.ERROR, UNKNOWN_ERROR);
+ map.put(
+ DOWNLOAD_STATE_INITIALIZATION_ERROR,
+ "The delta update payload was targeted for another version or the source partition"
+ + "was modified after it was installed");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+ "Failed to finish the configured postinstall works.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+ "Failed to open one of the partitions it tried to write to or read data from.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+ "Payload mismatch error.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+ "Failed to read the payload data from the given URL.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR, "Payload hash error.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+ "Payload size mismatch error.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+ "Failed to verify the signature of the payload.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+ "The payload has been successfully installed,"
+ + "but the active slot was not flipped.");
+ return map;
+ }
+
+ AbUpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor,
+ StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+ DevicePolicyConstants constants) {
+ super(context, updateFileDescriptor, callback, injector, constants);
+ mUpdateInstalled = false;
+ }
+
+ @Override
+ public void installUpdateInThread() {
+ if (mUpdateInstalled) {
+ throw new IllegalStateException("installUpdateInThread can be called only once.");
+ }
+ try {
+ setState();
+ applyPayload(Paths.get(mCopiedUpdateFile.getAbsolutePath()).toUri().toString());
+ } catch (ZipException e) {
+ Log.w(UpdateInstaller.TAG, e);
+ notifyCallbackOnError(
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+ Log.getStackTraceString(e));
+ } catch (IOException e) {
+ Log.w(UpdateInstaller.TAG, e);
+ notifyCallbackOnError(
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e));
+ }
+ }
+
+ private void setState() throws IOException {
+ mUpdateInstalled = true;
+ mPackedUpdateFile = new ZipFile(mCopiedUpdateFile);
+ mProperties = new ArrayList<>();
+ mSizeForUpdate = -1;
+ mOffsetForUpdate = 0;
+ mEntries = mPackedUpdateFile.entries();
+ }
+
+ private UpdateEngine buildBoundUpdateEngine() {
+ UpdateEngine updateEngine = new UpdateEngine();
+ updateEngine.bind(new DelegatingUpdateEngineCallback(this, updateEngine));
+ return updateEngine;
+ }
+
+ private void applyPayload(String updatePath) throws IOException {
+ if (!updateStateForPayload()) {
+ return;
+ }
+ String[] headerKeyValuePairs = mProperties.stream().toArray(String[]::new);
+ if (mSizeForUpdate == -1) {
+ Log.w(UpdateInstaller.TAG, "Failed to find payload entry in the given package.");
+ notifyCallbackOnError(
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+ "Failed to find payload entry in the given package.");
+ return;
+ }
+
+ UpdateEngine updateEngine = buildBoundUpdateEngine();
+ updateEngine.applyPayload(
+ updatePath, mOffsetForUpdate, mSizeForUpdate, headerKeyValuePairs);
+ }
+
+ private boolean updateStateForPayload() throws IOException {
+ long offset = 0;
+ while (mEntries.hasMoreElements()) {
+ ZipEntry entry = mEntries.nextElement();
+
+ String name = entry.getName();
+ offset += buildOffsetForEntry(entry, name);
+ if (entry.isDirectory()) {
+ offset -= entry.getCompressedSize();
+ continue;
+ }
+ if (PAYLOAD_BIN.equals(name)) {
+ if (entry.getMethod() != ZipEntry.STORED) {
+ Log.w(UpdateInstaller.TAG, "Invalid compression method.");
+ notifyCallbackOnError(
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+ "Invalid compression method.");
+ return false;
+ }
+ mSizeForUpdate = entry.getCompressedSize();
+ mOffsetForUpdate = offset - entry.getCompressedSize();
+ } else if (PAYLOAD_PROPERTIES_TXT.equals(name)) {
+ updatePropertiesForEntry(entry);
+ }
+ }
+ return true;
+ }
+
+ private long buildOffsetForEntry(ZipEntry entry, String name) {
+ return OFFSET_TO_FILE_NAME + name.length() + entry.getCompressedSize()
+ + (entry.getExtra() == null ? 0 : entry.getExtra().length);
+ }
+
+ private void updatePropertiesForEntry(ZipEntry entry) throws IOException {
+ try (BufferedReader bufferedReader = new BufferedReader(
+ new InputStreamReader(mPackedUpdateFile.getInputStream(entry)))) {
+ String line;
+ /* Neither @line nor @mProperties are size constraint since there is a few properties
+ with limited size. */
+ while ((line = bufferedReader.readLine()) != null) {
+ mProperties.add(line);
+ }
+ }
+ }
+
+ private static class DelegatingUpdateEngineCallback extends UpdateEngineCallback {
+ private UpdateInstaller mUpdateInstaller;
+ private UpdateEngine mUpdateEngine;
+
+ DelegatingUpdateEngineCallback(
+ UpdateInstaller updateInstaller, UpdateEngine updateEngine) {
+ mUpdateInstaller = updateInstaller;
+ mUpdateEngine = updateEngine;
+ }
+
+ @Override
+ public void onStatusUpdate(int statusCode, float percentage) {
+ return;
+ }
+
+ @Override
+ public void onPayloadApplicationComplete(int errorCode) {
+ mUpdateEngine.unbind();
+ if (errorCode == UpdateEngine.ErrorCodeConstants.SUCCESS) {
+ mUpdateInstaller.notifyCallbackOnSuccess();
+ } else {
+ mUpdateInstaller.notifyCallbackOnError(
+ errorCodesMap.getOrDefault(
+ errorCode, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN),
+ errorStringsMap.getOrDefault(errorCode, UNKNOWN_ERROR + errorCode));
+ }
+ }
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 2dbbf55..b9dabb9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -17,10 +17,15 @@
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
import android.content.ComponentName;
+import android.os.ParcelFileDescriptor;
import com.android.server.SystemService;
+import java.util.Collections;
+import java.util.List;
+
/**
* Defines the required interface for IDevicePolicyManager implemenation.
*
@@ -88,4 +93,31 @@
public String getGlobalPrivateDnsHost(ComponentName who) {
return null;
}
+
+ @Override
+ public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) { }
+
+ @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;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 71fea02..fd59b43 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -42,6 +42,12 @@
private static final String DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
"das_died_service_stable_connection_threshold_sec";
+ private static final String BATTERY_THRESHOLD_NOT_CHARGING_KEY =
+ "battery_threshold_not_charging";
+
+ private static final String BATTERY_THRESHOLD_CHARGING_KEY =
+ "battery_threshold_charging";
+
/**
* The back-off before re-connecting, when a service binding died, due to the owner
* crashing repeatedly.
@@ -63,6 +69,17 @@
*/
public final long DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;
+ /**
+ * Battery threshold for installing system update while the device is not charging.
+ */
+ public final int BATTERY_THRESHOLD_NOT_CHARGING;
+
+ /**
+ * Battery threshold for installing system update while the device is charging.
+ */
+ public final int BATTERY_THRESHOLD_CHARGING;
+
+
private DevicePolicyConstants(String settings) {
final KeyValueListParser parser = new KeyValueListParser(',');
@@ -87,6 +104,12 @@
DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY,
TimeUnit.MINUTES.toSeconds(2));
+ int batteryThresholdNotCharging = parser.getInt(
+ BATTERY_THRESHOLD_NOT_CHARGING_KEY, 40);
+
+ int batteryThresholdCharging = parser.getInt(
+ BATTERY_THRESHOLD_CHARGING_KEY, 20);
+
// Set minimum: 5 seconds.
dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec);
@@ -103,6 +126,8 @@
DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC = dasDiedServiceReconnectMaxBackoffSec;
DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC =
dasDiedServiceStableConnectionThresholdSec;
+ BATTERY_THRESHOLD_NOT_CHARGING = batteryThresholdNotCharging;
+ BATTERY_THRESHOLD_CHARGING = batteryThresholdCharging;
}
public static DevicePolicyConstants loadFromString(String settings) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a7542d7..6fbb850 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -55,16 +55,15 @@
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OFF;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
-
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Telephony.Carriers.DPC_URI;
@@ -75,11 +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;
@@ -117,6 +115,7 @@
import android.app.admin.PasswordMetrics;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
+import android.app.admin.StartInstallingUpdateCallback;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.backup.IBackupManager;
@@ -228,19 +227,19 @@
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.StatLogger;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
-import com.android.internal.util.StatLogger;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.UserRestrictionsUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
-
import com.android.server.uri.UriGrantsManagerInternal;
+
import com.google.android.collect.Sets;
import org.xmlpull.v1.XmlPullParser;
@@ -267,7 +266,6 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -384,6 +382,8 @@
private static final Set<String> GLOBAL_SETTINGS_DEPRECATED;
private static final Set<String> SYSTEM_SETTINGS_WHITELIST;
private static final Set<Integer> DA_DISALLOWED_POLICIES;
+ private static final String AB_DEVICE_KEY = "ro.build.ab_update";
+
static {
SECURE_SETTINGS_WHITELIST = new ArraySet<>();
SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -910,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;
@@ -1032,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;
@@ -1301,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,
@@ -1493,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);
@@ -1708,6 +1724,8 @@
pw.print(prefix); pw.println("parentAdmin:");
parentAdmin.dump(prefix + " ", pw);
}
+ pw.print(prefix); pw.print("mCrossProfileCalendarPackages=");
+ pw.println(mCrossProfileCalendarPackages);
}
}
@@ -2701,7 +2719,7 @@
final DevicePolicyData policy = getUserData(userId);
ActiveAdmin admin = policy.mAdminMap.get(who);
if (admin == null) {
- throw new SecurityException("No active admin " + who);
+ throw new SecurityException("No active admin " + who + " for UID " + uid);
}
if (admin.getUid() != uid) {
throw new SecurityException("Admin " + who + " is not owned by uid " + uid);
@@ -2709,6 +2727,16 @@
return admin;
}
+ /**
+ * Returns the active admin for the user of the caller as denoted by uid, which implements
+ * the {@code reqPolicy}.
+ *
+ * The {@code who} parameter is used as a hint:
+ * If provided, it must be the component name of the active admin for that user and the caller
+ * uid must match the uid of the admin.
+ * If not provided, iterate over all of the active admins in the DevicePolicyData for that user
+ * and return the one with the uid specified as parameter, and has the policy specified.
+ */
private ActiveAdmin getActiveAdminWithPolicyForUidLocked(ComponentName who, int reqPolicy,
int uid) {
ensureLocked();
@@ -5435,23 +5463,54 @@
return false;
}
- private void enforceIsDeviceOwnerOrCertInstallerOfDeviceOwner(
+ /**
+ * Enforce one the following conditions are met:
+ * (1) The device has a Device Owner, and one of the following holds:
+ * (1.1) The caller is the Device Owner
+ * (1.2) The caller is another app in the same user as the device owner, AND
+ * The caller is the delegated certificate installer.
+ * (2) The user has a profile owner, AND:
+ * (2.1) The profile owner has been granted access to Device IDs and one of the following
+ * holds:
+ * (2.1.1) The caller is the profile owner.
+ * (2.1.2) The caller is from another app in the same user as the profile owner, AND
+ * (2.1.2.1) The caller is the delegated cert installer.
+ *
+ * For the device owner case, simply check that the caller is the device owner or the
+ * delegated certificate installer.
+ *
+ * For the profile owner case, first check that the caller is the profile owner or can
+ * manage the DELEGATION_CERT_INSTALL scope.
+ * If that check succeeds, ensure the profile owner was granted access to device
+ * identifiers. The grant is transitive: The delegated cert installer is implicitly allowed
+ * access to device identifiers in this case as part of the delegation.
+ */
+ @VisibleForTesting
+ public void enforceCallerCanRequestDeviceIdAttestation(
ComponentName who, String callerPackage, int callerUid) throws SecurityException {
- if (who == null) {
- if (!mOwners.hasDeviceOwner()) {
- throw new SecurityException("Not in Device Owner mode.");
+ final int userId = UserHandle.getUserId(callerUid);
+
+ /**
+ * First check if there's a profile owner because the device could be in COMP mode (where
+ * there's a device owner and profile owner on the same device).
+ * If the caller is from the work profile, then it must be the PO or the delegate, and
+ * it must have the right permission to access device identifiers.
+ */
+ if (hasProfileOwner(userId)) {
+ // Make sure that the caller is the profile owner or delegate.
+ enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+ DELEGATION_CERT_INSTALL);
+ // Verify that the profile owner was granted access to Device IDs.
+ if (canProfileOwnerAccessDeviceIds(userId)) {
+ return;
}
- if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) {
- throw new SecurityException("Caller not from device owner user");
- }
- if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) {
- throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid() +
- "has no permission to generate keys.");
- }
- } else {
- // Caller provided - check it is the device owner.
- enforceDeviceOwner(who);
+ throw new SecurityException(
+ "Profile Owner is not allowed to access Device IDs.");
}
+
+ // If not, fall back to the device owner check.
+ enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+ DELEGATION_CERT_INSTALL);
}
@VisibleForTesting
@@ -5499,7 +5558,7 @@
final int callingUid = mInjector.binderGetCallingUid();
if (deviceIdAttestationRequired && attestationUtilsFlags.length > 0) {
- enforceIsDeviceOwnerOrCertInstallerOfDeviceOwner(who, callerPackage, callingUid);
+ enforceCallerCanRequestDeviceIdAttestation(who, callerPackage, callingUid);
} else {
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
DELEGATION_CERT_INSTALL);
@@ -7365,6 +7424,18 @@
return who != null && who.equals(profileOwner);
}
+ private boolean hasProfileOwner(int userId) {
+ synchronized (getLockObject()) {
+ return mOwners.hasProfileOwner(userId);
+ }
+ }
+
+ private boolean canProfileOwnerAccessDeviceIds(int userId) {
+ synchronized (getLockObject()) {
+ return mOwners.canProfileOwnerAccessDeviceIds(userId);
+ }
+ }
+
@Override
public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) {
if (!mHasFeature) {
@@ -11583,6 +11654,53 @@
return false;
}
+ private boolean hasGrantProfileOwnerDevcieIdAccessPermission() {
+ return mContext.checkCallingPermission(
+ android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) {
+ // As the caller is the system, it must specify the component name of the profile owner
+ // as a sanity / safety check.
+ Preconditions.checkNotNull(who);
+
+ if (!mHasFeature) {
+ return;
+ }
+
+ // Only privileged system apps can grant the Profile Owner access to Device IDs.
+ if (!(isCallerWithSystemUid() || isAdb()
+ || hasGrantProfileOwnerDevcieIdAccessPermission())) {
+ throw new SecurityException(
+ "Only the system can grant Device IDs access for a profile owner.");
+ }
+
+ if (isAdb() && hasIncompatibleAccountsOrNonAdbNoLock(userId, who)) {
+ throw new SecurityException(
+ "Can only be called from ADB if the device has no accounts.");
+ }
+
+ // Grant access under lock.
+ synchronized (getLockObject()) {
+ // Sanity check: Make sure that the user has a profile owner and that the specified
+ // component is the profile owner of that user.
+ if (!isProfileOwner(who, userId)) {
+ throw new IllegalArgumentException(String.format(
+ "Component %s is not a Profile Owner of user %d",
+ who.flattenToString(), userId));
+ }
+
+ Slog.i(LOG_TAG, String.format("Granting Device ID access to %s, for user %d",
+ who.flattenToString(), userId));
+
+ // setProfileOwnerCanAccessDeviceIds will trigger writing of the profile owner
+ // data, no need to do it manually.
+ mOwners.setProfileOwnerCanAccessDeviceIds(userId);
+ }
+ }
+
private void pushMeteredDisabledPackagesLocked(int userId) {
mInjector.getNetworkPolicyManagerInternal().setMeteredRestrictedPackages(
getMeteredDisabledPackagesLocked(userId), userId);
@@ -13220,4 +13338,98 @@
return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
}
+
+ @Override
+ public void installUpdateFromFile(ComponentName admin,
+ ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback callback) {
+ enforceDeviceOwner(admin);
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ UpdateInstaller updateInstaller;
+ if (isDeviceAB()) {
+ updateInstaller = new AbUpdateInstaller(
+ mContext, updateFileDescriptor, callback, mInjector, mConstants);
+ } else {
+ updateInstaller = new NonAbUpdateInstaller(
+ mContext, updateFileDescriptor, callback, mInjector, mConstants);
+ }
+ updateInstaller.startInstallUpdate();
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ 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 && admin.mCrossProfileCalendarPackages != null) {
+ return admin.mCrossProfileCalendarPackages.contains(packageName);
+ }
+ }
+ return false;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
new file mode 100644
index 0000000..5f1e926
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
@@ -0,0 +1,52 @@
+/*
+ * 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.devicepolicy;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.RecoverySystem;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Used for installing an update for <a href="https://source.android.com/devices/tech/ota/nonab">non
+ * AB</a> devices.
+ */
+class NonAbUpdateInstaller extends UpdateInstaller {
+ NonAbUpdateInstaller(Context context,
+ ParcelFileDescriptor updateFileDescriptor,
+ StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+ DevicePolicyConstants constants) {
+ super(context, updateFileDescriptor, callback, injector, constants);
+ }
+
+ @Override
+ public void installUpdateInThread() {
+ try {
+ RecoverySystem.installPackage(mContext, mCopiedUpdateFile);
+ notifyCallbackOnSuccess();
+ } catch (IOException e) {
+ Log.w(TAG, "IO error while trying to install non AB update.", e);
+ notifyCallbackOnError(
+ DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+ Log.getStackTraceString(e));
+ }
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 632f0aa..ee1c1df3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -42,6 +42,8 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.server.LocalServices;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -58,8 +60,6 @@
import java.util.Objects;
import java.util.Set;
-import libcore.io.IoUtils;
-
/**
* Stores and restores state for the Device and Profile owners and related device-wide information.
* By definition there can be only one device owner, but there may be a profile owner for each user.
@@ -99,6 +99,7 @@
private static final String ATTR_USER_RESTRICTIONS_MIGRATED = "userRestrictionsMigrated";
private static final String ATTR_FREEZE_RECORD_START = "start";
private static final String ATTR_FREEZE_RECORD_END = "end";
+ private static final String ATTR_CAN_ACCESS_DEVICE_IDS = "canAccessDeviceIds";
private final UserManager mUserManager;
private final UserManagerInternal mUserManagerInternal;
@@ -264,8 +265,12 @@
void setDeviceOwnerWithRestrictionsMigrated(ComponentName admin, String ownerName, int userId,
boolean userRestrictionsMigrated) {
synchronized (mLock) {
+ // A device owner is allowed to access device identifiers. Even though this flag
+ // is not currently checked for device owner, it is set to true here so that it is
+ // semantically compatible with the meaning of this flag.
mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated,
- /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null);
+ /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/
+ null, /* canAccessDeviceIds =*/true);
mDeviceOwnerUserId = userId;
mUserManagerInternal.setDeviceManaged(true);
@@ -290,7 +295,7 @@
// For a newly set PO, there's no need for migration.
mProfileOwners.put(userId, new OwnerInfo(ownerName, admin,
/* userRestrictionsMigrated =*/ true, /* remoteBugreportUri =*/ null,
- /* remoteBugreportHash =*/ null));
+ /* remoteBugreportHash =*/ null, /* canAccessDeviceIds =*/ false));
mUserManagerInternal.setUserManaged(userId, true);
pushToPackageManagerLocked();
pushToAppOpsLocked();
@@ -311,7 +316,8 @@
final OwnerInfo ownerInfo = mProfileOwners.get(userId);
final OwnerInfo newOwnerInfo = new OwnerInfo(target.getPackageName(), target,
ownerInfo.userRestrictionsMigrated, ownerInfo.remoteBugreportUri,
- ownerInfo.remoteBugreportHash);
+ ownerInfo.remoteBugreportHash, /* canAccessDeviceIds =*/
+ ownerInfo.canAccessDeviceIds);
mProfileOwners.put(userId, newOwnerInfo);
pushToPackageManagerLocked();
pushToAppOpsLocked();
@@ -324,7 +330,8 @@
// See DevicePolicyManagerService#getDeviceOwnerName
mDeviceOwner = new OwnerInfo(null, target,
mDeviceOwner.userRestrictionsMigrated, mDeviceOwner.remoteBugreportUri,
- mDeviceOwner.remoteBugreportHash);
+ mDeviceOwner.remoteBugreportHash, /* canAccessDeviceIds =*/
+ mDeviceOwner.canAccessDeviceIds);
pushToPackageManagerLocked();
pushToAppOpsLocked();
}
@@ -351,6 +358,17 @@
}
}
+ /**
+ * Returns true if {@code userId} has a profile owner and that profile owner was granted
+ * the ability to access device identifiers.
+ */
+ boolean canProfileOwnerAccessDeviceIds(int userId) {
+ synchronized (mLock) {
+ OwnerInfo profileOwner = mProfileOwners.get(userId);
+ return profileOwner != null ? profileOwner.canAccessDeviceIds : false;
+ }
+ }
+
Set<Integer> getProfileOwnerKeys() {
synchronized (mLock) {
return mProfileOwners.keySet();
@@ -486,6 +504,20 @@
}
}
+ /** Sets the grant to access device IDs, and also writes to file. */
+ void setProfileOwnerCanAccessDeviceIds(int userId) {
+ synchronized (mLock) {
+ OwnerInfo profileOwner = mProfileOwners.get(userId);
+ if (profileOwner != null) {
+ profileOwner.canAccessDeviceIds = true;
+ } else {
+ Slog.e(TAG, String.format(
+ "Cannot grant Device IDs access for user %d, no profile owner.", userId));
+ }
+ writeProfileOwner(userId);
+ }
+ }
+
private boolean readLegacyOwnerFileLocked(File file) {
if (!file.exists()) {
// Already migrated or the device has no owners.
@@ -507,7 +539,7 @@
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
mDeviceOwner = new OwnerInfo(name, packageName,
/* userRestrictionsMigrated =*/ false, /* remoteBugreportUri =*/ null,
- /* remoteBugreportHash =*/ null);
+ /* remoteBugreportHash =*/ null, /* canAccessDeviceIds =*/ true);
mDeviceOwnerUserId = UserHandle.USER_SYSTEM;
} else if (tag.equals(TAG_DEVICE_INITIALIZER)) {
// Deprecated tag
@@ -523,7 +555,8 @@
profileOwnerComponentStr);
if (admin != null) {
profileOwnerInfo = new OwnerInfo(profileOwnerName, admin,
- /* userRestrictionsMigrated =*/ false, null, null);
+ /* userRestrictionsMigrated =*/ false, null,
+ null, /* canAccessDeviceIds =*/ false);
} else {
// This shouldn't happen but switch from package name -> component name
// might have written bad device owner files. b/17652534
@@ -534,7 +567,8 @@
if (profileOwnerInfo == null) {
profileOwnerInfo = new OwnerInfo(profileOwnerName, profileOwnerPackageName,
/* userRestrictionsMigrated =*/ false,
- /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null);
+ /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/
+ null, /* canAccessDeviceIds =*/ false);
}
mProfileOwners.put(userId, profileOwnerInfo);
} else if (TAG_SYSTEM_UPDATE_POLICY.equals(tag)) {
@@ -894,25 +928,28 @@
public boolean userRestrictionsMigrated;
public String remoteBugreportUri;
public String remoteBugreportHash;
+ public boolean canAccessDeviceIds;
public OwnerInfo(String name, String packageName, boolean userRestrictionsMigrated,
- String remoteBugreportUri, String remoteBugreportHash) {
+ String remoteBugreportUri, String remoteBugreportHash, boolean canAccessDeviceIds) {
this.name = name;
this.packageName = packageName;
this.admin = new ComponentName(packageName, "");
this.userRestrictionsMigrated = userRestrictionsMigrated;
this.remoteBugreportUri = remoteBugreportUri;
this.remoteBugreportHash = remoteBugreportHash;
+ this.canAccessDeviceIds = canAccessDeviceIds;
}
public OwnerInfo(String name, ComponentName admin, boolean userRestrictionsMigrated,
- String remoteBugreportUri, String remoteBugreportHash) {
+ String remoteBugreportUri, String remoteBugreportHash, boolean canAccessDeviceIds) {
this.name = name;
this.admin = admin;
this.packageName = admin.getPackageName();
this.userRestrictionsMigrated = userRestrictionsMigrated;
this.remoteBugreportUri = remoteBugreportUri;
this.remoteBugreportHash = remoteBugreportHash;
+ this.canAccessDeviceIds = canAccessDeviceIds;
}
public void writeToXml(XmlSerializer out, String tag) throws IOException {
@@ -932,6 +969,10 @@
if (remoteBugreportHash != null) {
out.attribute(null, ATTR_REMOTE_BUGREPORT_HASH, remoteBugreportHash);
}
+ if (canAccessDeviceIds) {
+ out.attribute(null, ATTR_CAN_ACCESS_DEVICE_IDS,
+ String.valueOf(canAccessDeviceIds));
+ }
out.endTag(null, tag);
}
@@ -948,13 +989,17 @@
ATTR_REMOTE_BUGREPORT_URI);
final String remoteBugreportHash = parser.getAttributeValue(null,
ATTR_REMOTE_BUGREPORT_HASH);
+ final String canAccessDeviceIdsStr =
+ parser.getAttributeValue(null, ATTR_CAN_ACCESS_DEVICE_IDS);
+ final boolean canAccessDeviceIds =
+ ("true".equals(canAccessDeviceIdsStr));
// Has component name? If so, return [name, component]
if (componentName != null) {
final ComponentName admin = ComponentName.unflattenFromString(componentName);
if (admin != null) {
return new OwnerInfo(name, admin, userRestrictionsMigrated,
- remoteBugreportUri, remoteBugreportHash);
+ remoteBugreportUri, remoteBugreportHash, canAccessDeviceIds);
} else {
// This shouldn't happen but switch from package name -> component name
// might have written bad device owner files. b/17652534
@@ -965,13 +1010,14 @@
// Else, build with [name, package]
return new OwnerInfo(name, packageName, userRestrictionsMigrated, remoteBugreportUri,
- remoteBugreportHash);
+ remoteBugreportHash, canAccessDeviceIds);
}
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "admin=" + admin);
pw.println(prefix + "name=" + name);
pw.println(prefix + "package=" + packageName);
+ pw.println(prefix + "canAccessDeviceIds=" + canAccessDeviceIds);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
new file mode 100644
index 0000000..7910598
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
@@ -0,0 +1,146 @@
+/*
+ * 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.devicepolicy;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+abstract class UpdateInstaller {
+ private StartInstallingUpdateCallback mCallback;
+ private ParcelFileDescriptor mUpdateFileDescriptor;
+ private DevicePolicyConstants mConstants;
+ protected Context mContext;
+ protected File mCopiedUpdateFile;
+
+ static final String TAG = "UpdateInstaller";
+ private DevicePolicyManagerService.Injector mInjector;
+
+ protected UpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor,
+ StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+ DevicePolicyConstants constants) {
+ mContext = context;
+ mCallback = callback;
+ mUpdateFileDescriptor = updateFileDescriptor;
+ mInjector = injector;
+ mConstants = constants;
+ }
+
+ public abstract void installUpdateInThread();
+
+ public void startInstallUpdate() {
+ if (!checkIfBatteryIsSufficient()) {
+ notifyCallbackOnError(
+ DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW,
+ "The battery level must be above "
+ + mConstants.BATTERY_THRESHOLD_NOT_CHARGING + " while not charging or"
+ + "above " + mConstants.BATTERY_THRESHOLD_CHARGING + " while charging");
+ return;
+ }
+ Thread thread = new Thread(() -> {
+ mCopiedUpdateFile = copyUpdateFileToDataOtaPackageDir();
+ if (mCopiedUpdateFile == null) {
+ notifyCallbackOnError(
+ DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+ "Error while copying file.");
+ return;
+ }
+ installUpdateInThread();
+ });
+ thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ }
+
+ private boolean checkIfBatteryIsSufficient() {
+ BatteryManager batteryManager =
+ (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE);
+ if (batteryManager != null) {
+ int chargePercentage = batteryManager
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+ return batteryManager.isCharging()
+ ? chargePercentage >= mConstants.BATTERY_THRESHOLD_CHARGING
+ : chargePercentage >= mConstants.BATTERY_THRESHOLD_NOT_CHARGING;
+ }
+ return false;
+ }
+
+ private File copyUpdateFileToDataOtaPackageDir() {
+ try {
+ File destination = createNewFileWithPermissions();
+ copyToFile(destination);
+ return destination;
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to copy update file to OTA directory", e);
+ notifyCallbackOnError(
+ DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+ Log.getStackTraceString(e));
+ return null;
+ }
+ }
+
+ private File createNewFileWithPermissions() throws IOException {
+ File destination = File.createTempFile(
+ "update", ".zip", new File(Environment.getDataDirectory() + "/ota_package"));
+ FileUtils.setPermissions(
+ /* path= */ destination,
+ /* mode= */ FileUtils.S_IRWXU | FileUtils.S_IRGRP | FileUtils.S_IROTH,
+ /* uid= */ -1, /* gid= */ -1);
+ return destination;
+ }
+
+ private void copyToFile(File destination) throws IOException {
+ try (OutputStream out = new FileOutputStream(destination);
+ InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
+ mUpdateFileDescriptor)) {
+ FileUtils.copy(in, out);
+ }
+ }
+
+ void cleanupUpdateFile() {
+ if (mCopiedUpdateFile.exists()) {
+ mCopiedUpdateFile.delete();
+ }
+ }
+
+ protected void notifyCallbackOnError(int errorCode, String errorMessage) {
+ cleanupUpdateFile();
+ try {
+ mCallback.onStartInstallingUpdateError(errorCode, errorMessage);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while calling callback", e);
+ }
+ }
+
+ protected void notifyCallbackOnSuccess() {
+ cleanupUpdateFile();
+ mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER);
+ }
+}
diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
index 57e954f..08fbf55 100644
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -24,11 +24,14 @@
import android.service.intelligence.InteractionSessionId;
import android.service.intelligence.SnapshotData;
import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.AbstractRemoteService;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
import java.io.PrintWriter;
@@ -39,12 +42,12 @@
private static final String TAG = "ContentCaptureSession";
private final Object mLock;
- private final IBinder mActivityToken;
-
+ final IBinder mActivityToken;
private final IntelligencePerUserService mService;
private final RemoteIntelligenceService mRemoteService;
private final InteractionContext mInterationContext;
private final InteractionSessionId mId;
+ private AugmentedAutofillCallback mAutofillCallback;
ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
@NonNull IBinder activityToken, @NonNull IntelligencePerUserService service,
@@ -92,6 +95,18 @@
}
/**
+ * Requests the service to autofill the given field.
+ */
+ public AugmentedAutofillCallback requestAutofillLocked(@NonNull IAutoFillManagerClient client,
+ int autofillSessionId, @NonNull AutofillId focusedId) {
+ mRemoteService.onRequestAutofillLocked(mId, client, autofillSessionId, focusedId);
+ if (mAutofillCallback == null) {
+ mAutofillCallback = () -> mRemoteService.onDestroyAutofillWindowsRequest(mId);
+ }
+ return mAutofillCallback;
+ }
+
+ /**
* Cleans up the session and removes it from the service.
*
* @param notifyRemoteService whether it should trigger a {@link
@@ -119,6 +134,11 @@
if (mService.isVerbose()) {
Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
}
+ if (mAutofillCallback != null) {
+ mAutofillCallback.destroy();
+ mAutofillCallback = null;
+ }
+
// TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
if (notifyRemoteService) {
mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
@@ -152,6 +172,8 @@
pw.print(prefix); pw.print("id: "); mId.dump(pw); pw.println();
pw.print(prefix); pw.print("context: "); mInterationContext.dump(pw); pw.println();
pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
+ pw.print(prefix); pw.print("has autofill callback: ");
+ pw.println(mAutofillCallback != null);
}
@Override
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index a7f45ee..9fd797d 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -19,6 +19,7 @@
import static android.content.Context.INTELLIGENCE_MANAGER_SERVICE;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.content.ComponentName;
@@ -27,6 +28,8 @@
import android.os.IBinder;
import android.os.UserManager;
import android.service.intelligence.InteractionSessionId;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
import android.view.intelligence.IIntelligenceManager;
@@ -86,20 +89,6 @@
service.destroyLocked();
}
- /**
- * Notifies the intelligence service of new assist data for the given activity.
- *
- * @return {@code false} if there was no service set for the given user
- */
- private boolean sendActivityAssistDataLocked(@UserIdInt int userId,
- @NonNull IBinder activityToken, @NonNull Bundle data) {
- final IntelligencePerUserService service = peekServiceForUserLocked(userId);
- if (service != null) {
- return service.sendActivityAssistDataLocked(activityToken, data);
- }
- return false;
- }
-
private ActivityManagerInternal getAmInternal() {
synchronized (mLock) {
if (mAm == null) {
@@ -112,7 +101,7 @@
final class IntelligenceManagerServiceStub extends IIntelligenceManager.Stub {
@Override
- public void startSession(int userId, @NonNull IBinder activityToken,
+ public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken,
@NonNull ComponentName componentName, @NonNull InteractionSessionId sessionId,
int flags, @NonNull IResultReceiver result) {
Preconditions.checkNotNull(activityToken);
@@ -134,7 +123,7 @@
}
@Override
- public void sendEvents(int userId, @NonNull InteractionSessionId sessionId,
+ public void sendEvents(@UserIdInt int userId, @NonNull InteractionSessionId sessionId,
@NonNull List<ContentCaptureEvent> events) {
Preconditions.checkNotNull(sessionId);
Preconditions.checkNotNull(events);
@@ -146,12 +135,13 @@
}
@Override
- public void finishSession(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);
}
}
@@ -168,14 +158,13 @@
private final class LocalService extends IntelligenceManagerInternal {
@Override
- public boolean isIntelligenceServiceForUser(int uid, int userId) {
+ public boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId) {
synchronized (mLock) {
final IntelligencePerUserService service = peekServiceForUserLocked(userId);
if (service != null) {
return service.isIntelligenceServiceForUserLocked(uid);
}
}
-
return false;
}
@@ -183,8 +172,26 @@
public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken,
@NonNull Bundle data) {
synchronized (mLock) {
- return sendActivityAssistDataLocked(userId, activityToken, data);
+ final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.sendActivityAssistDataLocked(activityToken, data);
+ }
}
+ return false;
+ }
+
+ @Override
+ public AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
+ @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
+ int autofillSessionId, @NonNull AutofillId focusedId) {
+ synchronized (mLock) {
+ final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.requestAutofill(client, activityToken, autofillSessionId,
+ focusedId);
+ }
+ }
+ return null;
}
}
}
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
index 9694ab9..9ab7e58 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -22,6 +22,8 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
@@ -36,12 +38,15 @@
import android.service.intelligence.SnapshotData;
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 com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
import com.android.server.AbstractPerUserSystemService;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
import java.io.PrintWriter;
import java.util.List;
@@ -62,7 +67,7 @@
// TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
protected IntelligencePerUserService(
- IntelligenceManagerService master, Object lock, int userId) {
+ IntelligenceManagerService master, Object lock, @UserIdInt int userId) {
super(master, lock, userId);
}
@@ -142,7 +147,8 @@
// 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;
}
@@ -154,8 +160,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);
}
@@ -210,6 +226,17 @@
return uid == getServiceUidLocked();
}
+ @GuardedBy("mLock")
+ private ContentCaptureSession getSession(@NonNull IBinder activityToken) {
+ for (int i = 0; i < mSessions.size(); i++) {
+ final ContentCaptureSession session = mSessions.valueAt(i);
+ if (session.mActivityToken.equals(activityToken)) {
+ return session;
+ }
+ }
+ return null;
+ }
+
/**
* Destroys the service and all state associated with it.
*
@@ -226,6 +253,22 @@
mSessions.clear();
}
+ public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client,
+ @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) {
+ synchronized (mLock) {
+ final ContentCaptureSession session = getSession(activityToken);
+ if (session != null) {
+ // TODO(b/111330312): log metrics
+ if (mMaster.verbose) Slog.v(TAG, "requestAugmentedAutofill()");
+ return session.requestAutofillLocked(client, autofillSessionId, focusedId);
+ }
+ if (mMaster.debug) {
+ Slog.d(TAG, "requestAutofill(): no session for " + activityToken);
+ }
+ return null;
+ }
+ }
+
@Override
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
index a27c1cf..00c5b6a 100644
--- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
+++ b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
@@ -28,8 +29,12 @@
import android.service.intelligence.SnapshotData;
import android.text.format.DateUtils;
import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.IAutoFillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
+import com.android.internal.os.IResultReceiver;
import com.android.server.AbstractRemoteService;
import java.util.List;
@@ -39,7 +44,7 @@
private static final String TAG = "RemoteIntelligenceService";
private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
- private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
private final RemoteIntelligenceServiceCallbacks mCallbacks;
private IIntelligenceService mService;
@@ -101,6 +106,25 @@
scheduleRequest(new PendingOnActivitySnapshotRequest(this, sessionId, snapshotData));
}
+ /**
+ * Called by {@link ContentCaptureSession} to request augmented autofill.
+ */
+ public void onRequestAutofillLocked(@NonNull InteractionSessionId sessionId,
+ @NonNull IAutoFillManagerClient client, int autofillSessionId,
+ @NonNull AutofillId focusedId) {
+ cancelScheduledUnbind();
+ scheduleRequest(new PendingAutofillRequest(this, sessionId, client, autofillSessionId,
+ focusedId));
+ }
+
+ /**
+ * Called by {@link ContentCaptureSession} when it's time to destroy all augmented autofill
+ * requests.
+ */
+ public void onDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
+ cancelScheduledUnbind();
+ scheduleRequest(new PendingDestroyAutofillWindowsRequest(this, sessionId));
+ }
private abstract static class MyPendingRequest
extends PendingRequest<RemoteIntelligenceService> {
@@ -124,8 +148,9 @@
final RemoteIntelligenceService remoteService = getService();
if (remoteService != null) {
try {
- myRun(remoteService);
// We don't expect the service to call us back, so we finish right away.
+ myRun(remoteService);
+ // TODO(b/111330312): not true anymore!!
finish();
} catch (RemoteException e) {
Slog.w(TAG, "exception handling " + getClass().getSimpleName() + " for "
@@ -191,6 +216,53 @@
}
}
+ private static final class PendingAutofillRequest extends MyPendingRequest {
+ private final @NonNull AutofillId mFocusedId;
+ private final @NonNull IAutoFillManagerClient mClient;
+ private final int mAutofillSessionId;
+
+ protected PendingAutofillRequest(@NonNull RemoteIntelligenceService service,
+ @NonNull InteractionSessionId sessionId, @NonNull IAutoFillManagerClient client,
+ int autofillSessionId, @NonNull AutofillId focusedId) {
+ super(service, sessionId);
+ mClient = client;
+ mAutofillSessionId = autofillSessionId;
+ mFocusedId = focusedId;
+ }
+
+ @Override // from MyPendingRequest
+ public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+ final IResultReceiver receiver = new IResultReceiver.Stub() {
+
+ @Override
+ public void send(int resultCode, Bundle resultData) throws RemoteException {
+ final IBinder realClient = resultData
+ .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT);
+ remoteService.mService.onAutofillRequest(mSessionId, realClient,
+ mAutofillSessionId, mFocusedId);
+ }
+ };
+
+ // TODO(b/111330312): set cancellation signal, timeout (from both mClient and service),
+ // cache IAugmentedAutofillManagerClient reference, etc...
+ mClient.getAugmentedAutofillClient(receiver);
+ }
+ }
+
+ private static final class PendingDestroyAutofillWindowsRequest extends MyPendingRequest {
+
+ protected PendingDestroyAutofillWindowsRequest(@NonNull RemoteIntelligenceService service,
+ @NonNull InteractionSessionId sessionId) {
+ super(service, sessionId);
+ }
+
+ @Override
+ protected void myRun(@NonNull RemoteIntelligenceService service) throws RemoteException {
+ service.mService.onDestroyAutofillWindowsRequest(mSessionId);
+ // TODO(b/111330312): implement timeout
+ }
+ }
+
public interface RemoteIntelligenceServiceCallbacks extends VultureCallback {
// To keep it simple, we use the same callback for all failures / timeouts.
void onFailureOrTimeout(boolean timedOut);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f8ac41f..56f7cff 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,7 +36,6 @@
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
import android.hardware.display.ColorDisplayManager;
-import android.hardware.display.DisplayManagerInternal;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -717,17 +716,9 @@
// Manages Overlay packages
traceBeginAndSlog("StartOverlayManagerService");
- OverlayManagerService overlayManagerService = new OverlayManagerService(
- mSystemContext, installer);
- mSystemServiceManager.startService(overlayManagerService);
+ mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
traceEnd();
- if (SystemProperties.getInt("persist.sys.displayinset.top", 0) > 0) {
- // DisplayManager needs the overlay immediately.
- overlayManagerService.updateSystemUiContext();
- LocalServices.getService(DisplayManagerInternal.class).onOverlayChanged();
- }
-
// The sensor service needs access to package manager service, app ops
// service, and permissions service, therefore we start it after them.
// Start sensor service in a separate thread. Completion should be checked
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/Android.mk b/services/robotests/Android.mk
index 565152c..6f10ed5 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -26,6 +26,7 @@
LOCAL_PRIVILEGED_MODULE := true
LOCAL_STATIC_JAVA_LIBRARIES := \
+ bmgrlib \
services.backup \
services.core \
services.net
diff --git a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java b/services/robotests/src/com/android/commands/bmgr/BmgrTest.java
new file mode 100644
index 0000000..1705f5b
--- /dev/null
+++ b/services/robotests/src/com/android/commands/bmgr/BmgrTest.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 com.android.commands.bmgr;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+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;
+
+/** Unit tests for {@link com.android.commands.bmgr.Bmgr}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class BmgrTest {
+ @Mock private IBackupManager mBackupManager;
+ private Bmgr mBmgr;
+
+ /** Common setup run before each test method. */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mBmgr = new Bmgr(mBackupManager);
+ }
+
+ /**
+ * Test that bmgr uses the default user {@link UserHandle.USER_SYSTEM} if no user is specified.
+ */
+ @Test
+ public void testRun_whenUserNotSpecified_callsBackupManagerAsSystemUser() throws Exception {
+ mBmgr.run(new String[] {"run"});
+
+ verify(mBackupManager).isBackupServiceActive(UserHandle.USER_SYSTEM);
+ }
+
+ /** Test that bmgr uses the specified user if an user is specified. */
+ @Test
+ public void testRun_whenUserSpecified_callsBackupManagerAsSpecifiedUser() throws Exception {
+ mBmgr.run(new String[] {"--user", "10"});
+
+ verify(mBackupManager).isBackupServiceActive(10);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index 25d1cc7..ba4caf44 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,48 +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.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;
@@ -65,78 +44,44 @@
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() throws Exception {
@@ -145,897 +90,436 @@
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(ShadowBinder.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(ShadowBinder.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 603a471..bacc44e 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -18,9 +18,7 @@
import static com.android.server.backup.testing.TestUtils.runToEndOfTasks;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -36,43 +34,41 @@
import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
-import android.util.SparseArray;
+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.internal.Operation;
+import com.android.server.backup.UserBackupManagerService;
import org.mockito.stubbing.Answer;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowBinder;
-import org.robolectric.shadows.ShadowLog;
import java.io.File;
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,
@@ -84,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,
@@ -192,8 +189,7 @@
public static HandlerThread startSilentBackupThread(String tag) {
return startBackupThread(
(thread, e) ->
- ShadowLog.e(
- tag, "Uncaught exception in test thread " + thread.getName(), e));
+ Log.e(tag, "Uncaught exception in test thread " + thread.getName(), e));
}
private BackupManagerServiceTestUtils() {}
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
index 2f54513..3fe1f3f 100644
--- a/services/robotests/src/com/android/server/backup/testing/TestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
@@ -25,12 +25,12 @@
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
+import android.os.SystemClock;
import com.android.server.testing.shadows.ShadowEventLog;
import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowSystemClock;
import java.util.Arrays;
import java.util.concurrent.Callable;
@@ -87,7 +87,7 @@
// specific time to the looper the time of those messages will be before the looper's time.
// To fix this we advance SystemClock as well since that is from where the handlers read
// time.
- ShadowSystemClock.setCurrentTimeMillis(shadowLooper.getScheduler().getCurrentTime());
+ SystemClock.setCurrentTimeMillis(shadowLooper.getScheduler().getCurrentTime());
}
/**
diff --git a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
index aac0a34..a8a258f 100644
--- a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
@@ -5,6 +5,7 @@
import static org.mockito.Mockito.doReturn;
import android.os.Looper;
+import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.NtpTrustedTime;
@@ -18,7 +19,6 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowSystemClock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -74,7 +74,7 @@
doReturn(true).when(mMockNtpTrustedTime).forceRefresh();
doReturn(1L).when(mMockNtpTrustedTime).getCacheAge();
doReturn(MOCK_NTP_TIME).when(mMockNtpTrustedTime).getCachedNtpTime();
- ShadowSystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
+ SystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
waitForTasksToBePostedOnHandlerAndRunThem();
assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue();
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/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
new file mode 100644
index 0000000..ebc816d
--- /dev/null
+++ b/services/tests/mockingservicestests/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+android_test {
+ name: "FrameworksMockingServicesTests",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "services.core",
+ "services.net",
+ "androidx.test.runner",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ ],
+
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk
deleted file mode 100644
index b21b3e4..0000000
--- a/services/tests/mockingservicestests/Android.mk
+++ /dev/null
@@ -1,42 +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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- services.core \
- services.net \
- androidx.test.runner \
- mockito-target-extended-minus-junit4 \
- platform-test-annotations \
-
-LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner
-
-LOCAL_JNI_SHARED_LIBRARIES := \
- libdexmakerjvmtiagent \
- libstaticjvmtiagent \
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
new file mode 100644
index 0000000..e804342
--- /dev/null
+++ b/services/tests/servicestests/Android.bp
@@ -0,0 +1,104 @@
+//########################################################################
+// Build FrameworksServicesTests package
+//########################################################################
+
+android_test {
+ name: "FrameworksServicesTests",
+
+ // Include all test java files.
+ srcs: [
+ "src/**/*.java",
+
+ "aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl",
+ "aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl",
+
+ "test-apps/JobTestApp/src/**/*.java",
+
+ "test-apps/SuspendTestApp/src/**/*.java",
+ ],
+ static_libs: [
+ "frameworks-base-testutils",
+ "services.accessibility",
+ "services.appwidget",
+ "services.autofill",
+ "services.backup",
+ "services.core",
+ "services.devicepolicy",
+ "services.net",
+ "services.usage",
+ "guava",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "platform-test-annotations",
+ "ShortcutManagerTestUtils",
+ "truth-prebuilt",
+ "testables",
+ "testng",
+ "ub-uiautomator",
+ "platformprotosnano",
+ "hamcrest-library",
+ "servicestests-utils",
+ ],
+
+ aidl: {
+ local_include_dirs: ["aidl"],
+ },
+
+ libs: [
+ "android.hidl.manager-V1.0-java",
+ "android.hardware.tv.cec-V1.0-java",
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+
+ // These are not normally accessible from apps so they must be explicitly included.
+ jni_libs: [
+ "libbacktrace",
+ "libbase",
+ "libbinder",
+ "libbinderthreadstate",
+ "libc++",
+ "libcutils",
+ "liblog",
+ "liblzma",
+ "libnativehelper",
+ "libnetdaidl",
+ "libui",
+ "libunwind",
+ "libutils",
+ ],
+
+ dxflags: ["--multi-dex"],
+
+ optimize: {
+ enabled: false,
+ },
+}
+
+java_library {
+ name: "servicestests-utils",
+ srcs: [
+ "utils/**/*.java",
+ ],
+ static_libs: [
+ "android-support-test",
+ "mockito-target-minus-junit4",
+ ],
+ libs: [
+ "android.test.runner",
+ ],
+}
+
+filegroup {
+ name: "servicestests-SuspendTestApp-files",
+ srcs: [
+ "src/com/android/server/pm/SuspendPackagesTest.java",
+ ],
+}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
deleted file mode 100644
index e2f8995..0000000
--- a/services/tests/servicestests/Android.mk
+++ /dev/null
@@ -1,83 +0,0 @@
-#########################################################################
-# Build FrameworksServicesTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, utils) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- frameworks-base-testutils \
- services.accessibility \
- services.appwidget \
- services.autofill \
- services.backup \
- services.core \
- services.devicepolicy \
- services.net \
- services.usage \
- guava \
- androidx.test.runner \
- androidx.test.rules \
- mockito-target-minus-junit4 \
- platform-test-annotations \
- ShortcutManagerTestUtils \
- truth-prebuilt \
- testables \
- testng \
- ub-uiautomator\
- platformprotosnano \
- hamcrest-library
-
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
-
-LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl \
- aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl
-LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/JobTestApp/src)
-LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/SuspendTestApp/src)
-
-LOCAL_JAVA_LIBRARIES := \
- android.hidl.manager-V1.0-java \
- android.hardware.tv.cec-V1.0-java \
- android.test.mock \
- android.test.base android.test.runner \
-
-LOCAL_PACKAGE_NAME := FrameworksServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- libbacktrace \
- libbase \
- libbinder \
- libbinderthreadstate \
- libc++ \
- libcutils \
- liblog \
- liblzma \
- libnativehelper \
- libnetdaidl \
- libui \
- libunwind \
- libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/servicestests/aidl/Android.bp b/services/tests/servicestests/aidl/Android.bp
new file mode 100644
index 0000000..d4e53dd
--- /dev/null
+++ b/services/tests/servicestests/aidl/Android.bp
@@ -0,0 +1,22 @@
+// 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.
+
+java_library {
+ name: "servicestests-aidl",
+ sdk_version: "current",
+ srcs: [
+ "com/android/servicestests/aidl/INetworkStateObserver.aidl",
+ "com/android/servicestests/aidl/ICmdReceiverService.aidl",
+ ],
+}
diff --git a/services/tests/servicestests/aidl/Android.mk b/services/tests/servicestests/aidl/Android.mk
deleted file mode 100644
index 166da1d..0000000
--- a/services/tests/servicestests/aidl/Android.mk
+++ /dev/null
@@ -1,24 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := \
- com/android/servicestests/aidl/INetworkStateObserver.aidl \
- com/android/servicestests/aidl/ICmdReceiverService.aidl
-LOCAL_MODULE := servicestests-aidl
-include $(BUILD_STATIC_JAVA_LIBRARY)
\ 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/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/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 16b127c..5dc6d83 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4973,6 +4973,176 @@
assertProfileOwnershipRevertedWithFakeTransferMetadata();
}
+ public void testGrantDeviceIdsAccess_notToProfileOwner() throws Exception {
+ setupProfileOwner();
+ configureContextForAccess(mContext, false);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setProfileOwnerCanAccessDeviceIdsForUser(admin2,
+ UserHandle.of(DpmMockContext.CALLER_UID)));
+ }
+
+ public void testGrantDeviceIdsAccess_notByAuthorizedCaller() throws Exception {
+ setupProfileOwner();
+ configureContextForAccess(mContext, false);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setProfileOwnerCanAccessDeviceIdsForUser(admin1,
+ UserHandle.of(DpmMockContext.CALLER_UID)));
+ }
+
+ public void testGrantDeviceIdsAccess_byAuthorizedSystemCaller() throws Exception {
+ setupProfileOwner();
+
+ // This method will throw if the system context could not call
+ // setProfileOwnerCanAccessDeviceIds successfully.
+ configureProfileOwnerForDeviceIdAccess(admin1, DpmMockContext.CALLER_USER_HANDLE);
+ }
+
+ private static void configureContextForAccess(DpmMockContext context, boolean granted) {
+ when(context.spiedContext.checkCallingPermission(
+ android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS))
+ .thenReturn(granted ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED);
+ }
+
+ public void testGrantDeviceIdsAccess_byAuthorizedManagedProvisioning() throws Exception {
+ setupProfileOwner();
+
+ final long ident = mServiceContext.binder.clearCallingIdentity();
+ configureContextForAccess(mServiceContext, true);
+
+ mServiceContext.binder.callingUid =
+ UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.CALLER_MANAGED_PROVISIONING_UID);
+ try {
+ runAsCaller(mServiceContext, dpms, dpm -> {
+ dpm.setProfileOwnerCanAccessDeviceIdsForUser(admin1,
+ UserHandle.of(DpmMockContext.CALLER_USER_HANDLE));
+ });
+ } finally {
+ mServiceContext.binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void testEnforceCallerCanRequestDeviceIdAttestation_deviceOwnerCaller()
+ throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ configureContextForAccess(mContext, false);
+
+ // Device owner should be allowed to request Device ID attestation.
+ dpms.enforceCallerCanRequestDeviceIdAttestation(admin1, admin1.getPackageName(),
+ DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+ // Another package must not be allowed to request Device ID attestation.
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+ admin2.getPackageName(), DpmMockContext.CALLER_UID));
+ // Another component that is not the admin must not be allowed to request Device ID
+ // attestation.
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin2,
+ admin1.getPackageName(), DpmMockContext.CALLER_UID));
+ }
+
+ public void testEnforceCallerCanRequestDeviceIdAttestation_profileOwnerCaller()
+ throws Exception {
+ configureContextForAccess(mContext, false);
+
+ // Make sure a security exception is thrown if the device has no profile owner.
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin1,
+ admin1.getPackageName(), DpmMockContext.CALLER_SYSTEM_USER_UID));
+
+ setupProfileOwner();
+ configureProfileOwnerForDeviceIdAccess(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+ // The profile owner is allowed to request Device ID attestation.
+ mServiceContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ dpms.enforceCallerCanRequestDeviceIdAttestation(admin1, admin1.getPackageName(),
+ DpmMockContext.CALLER_UID);
+ // But not another package.
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+ admin2.getPackageName(), DpmMockContext.CALLER_UID));
+ // Or another component which is not the admin.
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin2,
+ admin2.getPackageName(), DpmMockContext.CALLER_UID));
+ }
+
+ public void runAsDelegatedCertInstaller(DpmRunnable action) throws Exception {
+ final long ident = mServiceContext.binder.clearCallingIdentity();
+
+ mServiceContext.binder.callingUid = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.DELEGATE_CERT_INSTALLER_UID);
+ try {
+ runAsCaller(mServiceContext, dpms, action);
+ } finally {
+ mServiceContext.binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void testEnforceCallerCanRequestDeviceIdAttestation_delegateCaller() throws Exception {
+ setupProfileOwner();
+ markDelegatedCertInstallerAsInstalled();
+
+ // Configure a delegated cert installer.
+ runAsCaller(mServiceContext, dpms,
+ dpm -> dpm.setDelegatedScopes(admin1, DpmMockContext.DELEGATE_PACKAGE_NAME,
+ Arrays.asList(DELEGATION_CERT_INSTALL)));
+
+ configureProfileOwnerForDeviceIdAccess(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+ // Make sure that the profile owner can still request Device ID attestation.
+ mServiceContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ dpms.enforceCallerCanRequestDeviceIdAttestation(admin1, admin1.getPackageName(),
+ DpmMockContext.CALLER_UID);
+
+ runAsDelegatedCertInstaller(dpm -> {
+ dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+ DpmMockContext.DELEGATE_PACKAGE_NAME,
+ UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.DELEGATE_CERT_INSTALLER_UID));
+ });
+ }
+
+ public void testEnforceCallerCanRequestDeviceIdAttestation_delegateCallerWithoutPermissions()
+ throws Exception {
+ setupProfileOwner();
+ markDelegatedCertInstallerAsInstalled();
+
+ // Configure a delegated cert installer.
+ runAsCaller(mServiceContext, dpms,
+ dpm -> dpm.setDelegatedScopes(admin1, DpmMockContext.DELEGATE_PACKAGE_NAME,
+ Arrays.asList(DELEGATION_CERT_INSTALL)));
+
+
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin1,
+ admin1.getPackageName(),
+ DpmMockContext.CALLER_UID));
+
+ runAsDelegatedCertInstaller(dpm -> {
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+ DpmMockContext.DELEGATE_PACKAGE_NAME,
+ UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.DELEGATE_CERT_INSTALLER_UID)));
+ });
+ }
+
+ private void configureProfileOwnerForDeviceIdAccess(ComponentName who, int userId) {
+ final long ident = mServiceContext.binder.clearCallingIdentity();
+ mServiceContext.binder.callingUid =
+ UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
+ runAsCaller(mServiceContext, dpms, dpm -> {
+ dpm.setProfileOwnerCanAccessDeviceIdsForUser(who, UserHandle.of(userId));
+ });
+ mServiceContext.binder.restoreCallingIdentity(ident);
+ }
+
// admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index be00bb6..e411fb5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -59,6 +59,12 @@
public static final int CALLER_UID = UserHandle.getUid(CALLER_USER_HANDLE, 20123);
/**
+ * UID corresponding to {@link #CALLER_USER_HANDLE}.
+ */
+ public static final int CALLER_MANAGED_PROVISIONING_UID = UserHandle.getUid(CALLER_USER_HANDLE,
+ 20125);
+
+ /**
* UID used when a caller is on the system user.
*/
public static final int CALLER_SYSTEM_USER_UID = 20321;
@@ -81,6 +87,10 @@
public static final String ANOTHER_PACKAGE_NAME = "com.another.package.name";
public static final int ANOTHER_UID = UserHandle.getUid(UserHandle.USER_SYSTEM, 18434);
+ public static final String DELEGATE_PACKAGE_NAME = "com.delegate.package.name";
+ public static final int DELEGATE_CERT_INSTALLER_UID = UserHandle.getUid(UserHandle.USER_SYSTEM,
+ 18437);
+
private final MockSystemServices mMockSystemServices;
public static class MockBinder {
@@ -427,4 +437,9 @@
public int getUserId() {
return UserHandle.getUserId(binder.getCallingUid());
}
+
+ @Override
+ public int checkCallingPermission(String permission) {
+ return spiedContext.checkCallingPermission(permission);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 0c8a787..a34c2ff 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -50,6 +50,7 @@
public ComponentName admin3;
public ComponentName adminAnotherPackage;
public ComponentName adminNoPerm;
+ public ComponentName delegateCertInstaller;
@Override
protected void setUp() throws Exception {
@@ -66,6 +67,8 @@
adminAnotherPackage = new ComponentName(DpmMockContext.ANOTHER_PACKAGE_NAME,
"whatever.random.class");
adminNoPerm = new ComponentName(mRealTestContext, DummyDeviceAdmins.AdminNoPerm.class);
+ delegateCertInstaller = new ComponentName(DpmMockContext.DELEGATE_PACKAGE_NAME,
+ "some.random.class");
mockSystemPropertiesToReturnDefault();
}
@@ -130,6 +133,20 @@
eq(userId));
}
+ protected void markDelegatedCertInstallerAsInstalled() throws Exception {
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ ai.flags = ApplicationInfo.FLAG_HAS_CODE;
+ // Mark the package as installed on the work profile.
+ ai.uid = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.DELEGATE_CERT_INSTALLER_UID);
+ ai.packageName = delegateCertInstaller.getPackageName();
+ ai.name = delegateCertInstaller.getClassName();
+
+ markPackageAsInstalled(delegateCertInstaller.getPackageName(), ai,
+ DpmMockContext.CALLER_USER_HANDLE);
+ }
+
protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid)
throws Exception {
setUpPackageManagerForAdmin(admin, packageUid,
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 58c4bbf..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<>();
@@ -494,7 +496,8 @@
pkg.usesLibraryFiles = new String[] { "foo13"};
pkg.usesLibraryInfos = new ArrayList<>();
- pkg.usesLibraryInfos.add(new SharedLibraryInfo(null, null, null, 0L, 0, null, null, null));
+ pkg.usesLibraryInfos.add(
+ new SharedLibraryInfo(null, null, null, null, 0L, 0, null, null, null));
pkg.mOriginalPackages = new ArrayList<>();
pkg.mOriginalPackages.add("foo14");
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/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index cd15a57..813fa82 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.fail;
import android.content.pm.ApplicationInfo;
+import android.content.pm.SharedLibraryInfo;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
@@ -39,6 +40,7 @@
import java.io.File;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -108,22 +110,31 @@
return data;
}
+ private List<SharedLibraryInfo> createMockSharedLibrary(String [] sharedLibrary) {
+ SharedLibraryInfo info = new SharedLibraryInfo(null, null, Arrays.asList(sharedLibrary),
+ null, 0L, SharedLibraryInfo.TYPE_STATIC, null, null, null);
+ ArrayList<SharedLibraryInfo> libraries = new ArrayList<>();
+ libraries.add(info);
+ return libraries;
+ }
+
@Test
public void testSplitChain() {
TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
- String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ List<SharedLibraryInfo> sharedLibrary =
+ createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
String[] contexts = DexoptUtils.getClassLoaderContexts(
data.info, sharedLibrary, data.pathsWithCode);
assertEquals(9, contexts.length);
- assertEquals("PCL[a.dex:b.dex]", contexts[0]);
- assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]",
+ assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
+ assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}",
contexts[1]);
- assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
- assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
- assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
- assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
- assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals("DLC[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[2]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[3]);
+ assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[4]);
+ assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[5]);
+ assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[6]);
assertEquals(null, contexts[7]); // config split
assertEquals("PCL[]", contexts[8]); // feature split with no dependency
}
@@ -131,25 +142,28 @@
@Test
public void testSplitChainNoSplitDependencies() {
TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
- String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ List<SharedLibraryInfo> sharedLibrary =
+ createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
String[] contexts = DexoptUtils.getClassLoaderContexts(
data.info, sharedLibrary, data.pathsWithCode);
assertEquals(9, contexts.length);
- assertEquals("PCL[a.dex:b.dex]", contexts[0]);
- assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]);
- assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]);
- assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex]", contexts[3]);
- assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex]", contexts[4]);
+ assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
+ assertEquals("PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[1]);
+ assertEquals("PCL[base.dex:base-1.dex]{PCL[a.dex:b.dex]}", contexts[2]);
+ assertEquals("PCL[base.dex:base-1.dex:base-2.dex]{PCL[a.dex:b.dex]}", contexts[3]);
assertEquals(
- "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]",
+ "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex]{PCL[a.dex:b.dex]}",
+ contexts[4]);
+ assertEquals(
+ "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]{PCL[a.dex:b.dex]}",
contexts[5]);
assertEquals(
- "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
+ "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]{PCL[a.dex:b.dex]}",
contexts[6]);
assertEquals(null, contexts[7]); // config split
assertEquals(
- "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
+ "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]{PCL[a.dex:b.dex]}",
contexts[8]); // feature split with no dependency
}
@@ -200,18 +214,21 @@
public void testSplitChainWithNullPrimaryClassLoader() {
// A null classLoaderName should mean PathClassLoader.
TestData data = createMockApplicationInfo(null, true, true);
- String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ List<SharedLibraryInfo> sharedLibrary =
+ createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
String[] contexts = DexoptUtils.getClassLoaderContexts(
data.info, sharedLibrary, data.pathsWithCode);
assertEquals(9, contexts.length);
- assertEquals("PCL[a.dex:b.dex]", contexts[0]);
- assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
- assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
- assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
- assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
- assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
- assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
+ assertEquals(
+ "DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}",
+ contexts[1]);
+ assertEquals("DLC[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[2]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[3]);
+ assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[4]);
+ assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[5]);
+ assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[6]);
assertEquals(null, contexts[7]); // config split
assertEquals("PCL[]", contexts[8]); // feature split with no dependency
}
@@ -219,35 +236,38 @@
@Test
public void tesNoSplits() {
TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
- String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ List<SharedLibraryInfo> sharedLibrary =
+ createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
String[] contexts = DexoptUtils.getClassLoaderContexts(
data.info, sharedLibrary, data.pathsWithCode);
assertEquals(1, contexts.length);
- assertEquals("PCL[a.dex:b.dex]", contexts[0]);
+ assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
}
@Test
public void tesNoSplitsNullClassLoaderName() {
TestData data = createMockApplicationInfo(null, false, false);
- String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ List<SharedLibraryInfo> sharedLibrary =
+ createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
String[] contexts = DexoptUtils.getClassLoaderContexts(
data.info, sharedLibrary, data.pathsWithCode);
assertEquals(1, contexts.length);
- assertEquals("PCL[a.dex:b.dex]", contexts[0]);
+ assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
}
@Test
public void tesNoSplitDelegateLast() {
TestData data = createMockApplicationInfo(
DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
- String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ List<SharedLibraryInfo> sharedLibrary =
+ createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
String[] contexts = DexoptUtils.getClassLoaderContexts(
data.info, sharedLibrary, data.pathsWithCode);
assertEquals(1, contexts.length);
- assertEquals("DLC[a.dex:b.dex]", contexts[0]);
+ assertEquals("DLC[]{PCL[a.dex:b.dex]}", contexts[0]);
}
@Test
@@ -276,7 +296,8 @@
TestData data = createMockApplicationInfo(null, true, false);
Arrays.fill(data.pathsWithCode, false);
- String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ List<SharedLibraryInfo> sharedLibrary =
+ createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
String[] contexts = DexoptUtils.getClassLoaderContexts(
data.info, sharedLibrary, data.pathsWithCode);
@@ -295,18 +316,21 @@
public void testContextBaseNoCode() {
TestData data = createMockApplicationInfo(null, true, true);
data.pathsWithCode[0] = false;
- String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ List<SharedLibraryInfo> sharedLibrary =
+ createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
String[] contexts = DexoptUtils.getClassLoaderContexts(
data.info, sharedLibrary, data.pathsWithCode);
assertEquals(9, contexts.length);
assertEquals(null, contexts[0]);
- assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
- assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
- assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
- assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
- assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
- assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals(
+ "DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}",
+ contexts[1]);
+ assertEquals("DLC[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[2]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[3]);
+ assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[4]);
+ assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[5]);
+ assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[6]);
assertEquals(null, contexts[7]);
}
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
new file mode 100644
index 0000000..7cf7df13
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -0,0 +1,299 @@
+/*
+ * 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.power;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.IThermalEventListener;
+import android.os.IThermalStatusListener;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.Temperature;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.SystemService;
+import com.android.server.power.ThermalManagerService.ThermalHalWrapper;
+
+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.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server
+ * /power/ThermalManagerServiceTest.java
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ThermalManagerServiceTest {
+ private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
+ private ThermalManagerService mService;
+ private ThermalHalFake mFakeHal;
+ private PowerManager mPowerManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private IPowerManager mIPowerManagerMock;
+ @Mock
+ private IThermalEventListener mEventListener1;
+ @Mock
+ private IThermalEventListener mEventListener2;
+ @Mock
+ private IThermalStatusListener mStatusListener1;
+ @Mock
+ private IThermalStatusListener mStatusListener2;
+
+ /**
+ * Fake Hal class.
+ */
+ private class ThermalHalFake extends ThermalHalWrapper {
+ private static final int INIT_STATUS = Temperature.THROTTLING_NONE;
+ private ArrayList<Temperature> mTemperatureList = new ArrayList<>();
+ private Temperature mSkin1 = new Temperature(0, Temperature.TYPE_SKIN, "skin1",
+ INIT_STATUS);
+ private Temperature mSkin2 = new Temperature(0, Temperature.TYPE_SKIN, "skin2",
+ INIT_STATUS);
+ private Temperature mBattery = new Temperature(0, Temperature.TYPE_BATTERY, "batt",
+ INIT_STATUS);
+ private Temperature mUsbPort = new Temperature(0, Temperature.TYPE_USB_PORT, "usbport",
+ INIT_STATUS);
+
+ ThermalHalFake() {
+ mTemperatureList.add(mSkin1);
+ mTemperatureList.add(mSkin2);
+ mTemperatureList.add(mBattery);
+ mTemperatureList.add(mUsbPort);
+ }
+
+ @Override
+ protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) {
+ return mTemperatureList;
+ }
+
+ @Override
+ protected boolean connectToHal() {
+ return true;
+ }
+
+ @Override
+ protected void dump(PrintWriter pw, String prefix) {
+ return;
+ }
+ }
+
+ private void assertTemperatureEquals(List<Temperature> expected, List<Temperature> value) {
+ assertEquals(new HashSet<>(expected), new HashSet<>(value));
+ }
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ mFakeHal = new ThermalHalFake();
+ mPowerManager = new PowerManager(mContext, mIPowerManagerMock, null);
+ when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
+ when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
+ resetListenerMock();
+ mService = new ThermalManagerService(mContext, mFakeHal);
+ // Register callbacks before AMS ready and no callback sent
+ assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+ assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+ Temperature.TYPE_SKIN));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).notifyThrottling(any(Temperature.class));
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).notifyThrottling(any(Temperature.class));
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(4)).notifyThrottling(captor.capture());
+ assertTemperatureEquals(mFakeHal.mTemperatureList, captor.getAllValues());
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ captor = ArgumentCaptor.forClass(Temperature.class);
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(2)).notifyThrottling(captor.capture());
+ assertTemperatureEquals(new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)),
+ captor.getAllValues());
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ }
+
+ private void resetListenerMock() {
+ reset(mEventListener1);
+ reset(mStatusListener1);
+ reset(mEventListener2);
+ reset(mStatusListener2);
+ doReturn(mock(IBinder.class)).when(mEventListener1).asBinder();
+ doReturn(mock(IBinder.class)).when(mStatusListener1).asBinder();
+ doReturn(mock(IBinder.class)).when(mEventListener2).asBinder();
+ doReturn(mock(IBinder.class)).when(mStatusListener2).asBinder();
+ }
+
+ @Test
+ public void testRegister() throws RemoteException {
+ // Unregister all
+ assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
+ assertTrue(mService.mService.unregisterThermalEventListener(mEventListener2));
+ assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener2));
+ resetListenerMock();
+ // Register callbacks and verify they are called
+ assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+ ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(4)).notifyThrottling(captor.capture());
+ assertTemperatureEquals(mFakeHal.mTemperatureList, captor.getAllValues());
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ // Register new callbacks and verify old ones are not called (remained same) while new
+ // ones are called
+ assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+ Temperature.TYPE_SKIN));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(4)).notifyThrottling(any(Temperature.class));
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ captor = ArgumentCaptor.forClass(Temperature.class);
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(2)).notifyThrottling(captor.capture());
+ assertTemperatureEquals(new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)),
+ captor.getAllValues());
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ }
+
+ @Test
+ public void testNotify() throws RemoteException {
+ int status = Temperature.THROTTLING_SEVERE;
+ Temperature newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
+ mFakeHal.mCallback.onValues(newBattery);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newBattery);
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).notifyThrottling(newBattery);
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ resetListenerMock();
+ // Should only notify event not status
+ Temperature newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onValues(newSkin);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newSkin);
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newSkin);
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ resetListenerMock();
+ // Back to None, should only notify event not status
+ status = Temperature.THROTTLING_NONE;
+ newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
+ mFakeHal.mCallback.onValues(newBattery);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newBattery);
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).notifyThrottling(newBattery);
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ resetListenerMock();
+ // Should also notify status
+ newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onValues(newSkin);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newSkin);
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newSkin);
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ }
+
+ @Test
+ public void testGetCurrentTemperatures() throws RemoteException {
+ assertTemperatureEquals(mFakeHal.getCurrentTemperatures(false, 0),
+ mService.mService.getCurrentTemperatures());
+ assertTemperatureEquals(mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN),
+ mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN));
+ }
+
+ @Test
+ public void testGetCurrentStatus() throws RemoteException {
+ int status = Temperature.THROTTLING_WARNING;
+ Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onValues(newSkin);
+ assertEquals(status, mService.mService.getCurrentStatus());
+ }
+
+ @Test
+ public void testThermalShutdown() throws RemoteException {
+ int status = Temperature.THROTTLING_SHUTDOWN;
+ Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onValues(newSkin);
+ verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
+ }
+
+ @Test
+ public void testNoHal() throws RemoteException {
+ mService = new ThermalManagerService(mContext);
+ // Do no call onActivityManagerReady to skip connect HAL
+ assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+ assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
+ assertEquals(0, mService.mService.getCurrentTemperatures().size());
+ assertEquals(0,
+ mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
+ assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentStatus());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
index f12619c..19d18ca 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
@@ -35,7 +35,6 @@
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 +110,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 +124,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 +166,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/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/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 552390d..99abbf7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/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,8 +32,11 @@
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 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;
@@ -66,6 +70,8 @@
Task mTask;
WindowTestUtils.TestAppWindowToken mToken;
+ private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
+
@Before
public void setUp() throws Exception {
mStack = createTaskStackOnDisplay(mDisplayContent);
@@ -251,7 +257,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 +267,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.mService.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 +409,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/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 991981f..a6415d1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -145,7 +145,7 @@
}
@Test
- public void testUpdateDimsAppliesSize() {
+ public void testUpdateDimsAppliesCrop() {
mDimmer.dimAbove(mTransaction, 0.8f);
int width = 100;
@@ -153,7 +153,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 +242,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/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
similarity index 71%
rename from services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
rename to services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index f024fe7..18bd2e4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.wm;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
@@ -25,36 +25,27 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.view.Display;
import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
+import org.junit.runner.RunWith;
-/**
- * Build/Install/Run:
- * atest WmTests:PhoneWindowManagerInsetsTest
- */
+@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
-public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase {
+public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
@Rule
public final ErrorCollector mErrorCollector = new ErrorCollector();
- @Before
- public void setUp() throws Exception {
- addStatusBar();
- addNavigationBar();
- }
-
@Test
public void portrait() {
- DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
@@ -63,7 +54,7 @@
@Test
public void portrait_withCutout() {
- DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
verifyNonDecorInsets(di, 0, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT);
@@ -72,7 +63,7 @@
@Test
public void landscape() {
- DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
verifyNonDecorInsets(di, 0, 0, NAV_BAR_HEIGHT, 0);
@@ -81,7 +72,7 @@
@Test
public void landscape_withCutout() {
- DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */);
verifyStableInsets(di, DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
verifyNonDecorInsets(di, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT, 0);
@@ -90,7 +81,7 @@
@Test
public void seascape() {
- DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */);
verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, 0, 0);
@@ -99,7 +90,7 @@
@Test
public void seascape_withCutout() {
- DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */);
verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, DISPLAY_CUTOUT_HEIGHT, 0);
@@ -108,7 +99,7 @@
@Test
public void upsideDown() {
- DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
@@ -117,7 +108,7 @@
@Test
public void upsideDown_withCutout() {
- DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
@@ -151,35 +142,39 @@
private Rect getStableInsetsLw(DisplayInfo di) {
Rect result = new Rect();
- mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ mDisplayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
di.displayCutout, result);
return result;
}
private Rect getNonDecorInsetsLw(DisplayInfo di) {
Rect result = new Rect();
- mPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
di.displayCutout, result);
return result;
}
private int getNonDecorDisplayWidth(DisplayInfo di) {
- return mPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight, di.rotation,
- 0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+ return mDisplayPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight,
+ di.rotation, 0 /* ui */, di.displayCutout);
}
private int getNonDecorDisplayHeight(DisplayInfo di) {
- return mPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight, di.rotation,
- 0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+ return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight,
+ di.rotation, 0 /* ui */, di.displayCutout);
}
private int getConfigDisplayWidth(DisplayInfo di) {
- return mPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight, di.rotation,
- 0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+ return mDisplayPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight,
+ di.rotation, 0 /* ui */, di.displayCutout);
}
private int getConfigDisplayHeight(DisplayInfo di) {
- return mPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight, di.rotation,
- 0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+ return mDisplayPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight,
+ di.rotation, 0 /* ui */, di.displayCutout);
+ }
+
+ private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
+ return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
new file mode 100644
index 0000000..a91c5e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -0,0 +1,422 @@
+/*
+ * 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/DisplayPolicyTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java
new file mode 100644
index 0000000..07d5fea
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -0,0 +1,202 @@
+/*
+ * 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.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+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.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.graphics.PixelFormat;
+import android.platform.test.annotations.Presubmit;
+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 {
+
+ private WindowState createOpaqueFullscreen(boolean hasLightNavBar) {
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "opaqueFullscreen");
+ final WindowManager.LayoutParams attrs = win.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.OPAQUE;
+ attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility =
+ hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+ return win;
+ }
+
+ private WindowState createDimmingDialogWindow(boolean canBeImTarget) {
+ final WindowState win = spy(createWindow(null, TYPE_APPLICATION, "dimmingDialog"));
+ final WindowManager.LayoutParams attrs = win.mAttrs;
+ attrs.width = WRAP_CONTENT;
+ attrs.height = WRAP_CONTENT;
+ attrs.flags = FLAG_DIM_BEHIND | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM);
+ attrs.format = PixelFormat.TRANSLUCENT;
+ when(win.isDimming()).thenReturn(true);
+ return win;
+ }
+
+ private WindowState createInputMethodWindow(boolean visible, boolean drawNavBar,
+ boolean hasLightNavBar) {
+ final WindowState win = createWindow(null, TYPE_INPUT_METHOD, "inputMethod");
+ final WindowManager.LayoutParams attrs = win.mAttrs;
+ attrs.width = MATCH_PARENT;
+ attrs.height = MATCH_PARENT;
+ attrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN
+ | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0);
+ attrs.format = PixelFormat.TRANSPARENT;
+ attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility =
+ hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+ win.mHasSurface = visible;
+ return win;
+ }
+
+ @Test
+ public void testChooseNavigationColorWindowLw() {
+ final WindowState opaque = createOpaqueFullscreen(false);
+
+ final WindowState dimmingImTarget = createDimmingDialogWindow(true);
+ final WindowState dimmingNonImTarget = createDimmingDialogWindow(false);
+
+ final WindowState visibleIme = createInputMethodWindow(true, true, false);
+ final WindowState invisibleIme = createInputMethodWindow(false, true, false);
+ final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
+
+ // If everything is null, return null
+ assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw(
+ null, null, null, NAV_BAR_BOTTOM));
+
+ assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, opaque, null, NAV_BAR_BOTTOM));
+ assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, dimmingImTarget, null, NAV_BAR_BOTTOM));
+ assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM));
+
+ assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+ null, null, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+ null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+ null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+
+ assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
+ assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
+ assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, opaque, visibleIme, NAV_BAR_RIGHT));
+
+ // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
+ // window.
+ assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+ opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ }
+
+ @Test
+ public void testUpdateLightNavigationBarLw() {
+ final WindowState opaqueDarkNavBar = createOpaqueFullscreen(false);
+ final WindowState opaqueLightNavBar = createOpaqueFullscreen(true);
+
+ final WindowState dimming = createDimmingDialogWindow(false);
+
+ final WindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false);
+ final WindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true);
+
+ assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ DisplayPolicy.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null,
+ null, null));
+
+ // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag.
+ assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ 0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
+ assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null,
+ opaqueDarkNavBar));
+ assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ DisplayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar,
+ opaqueLightNavBar, null, opaqueLightNavBar));
+ assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ DisplayPolicy.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
+
+ // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
+ assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ 0, opaqueDarkNavBar, dimming, null, dimming));
+ assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ 0, opaqueLightNavBar, dimming, null, dimming));
+ assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming));
+ assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming));
+ assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar,
+ dimming));
+
+ // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
+ assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar,
+ imeDrawDarkNavBar));
+
+ // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins.
+ assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar,
+ imeDrawDarkNavBar, imeDrawDarkNavBar));
+
+ // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
+ assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ DisplayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
+ opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java
new file mode 100644
index 0000000..1d63c57
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -0,0 +1,184 @@
+/*
+ * 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.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.os.IBinder;
+import android.testing.TestableResources;
+import android.util.Pair;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.R;
+import com.android.server.wm.utils.WmDisplayCutout;
+
+import org.junit.Before;
+
+public class DisplayPolicyTestsBase extends WindowTestsBase {
+
+ static final int DISPLAY_WIDTH = 500;
+ static final int DISPLAY_HEIGHT = 1000;
+ static final int DISPLAY_DENSITY = 320;
+
+ static final int STATUS_BAR_HEIGHT = 10;
+ static final int NAV_BAR_HEIGHT = 15;
+ static final int DISPLAY_CUTOUT_HEIGHT = 8;
+
+ DisplayPolicy mDisplayPolicy;
+
+ @Before
+ public void setUpBase() {
+ super.setUpBase();
+ mDisplayPolicy = spy(mDisplayContent.getDisplayPolicy());
+
+ final TestContextWrapper context =
+ new TestContextWrapper(mDisplayPolicy.getSystemUiContext());
+ final TestableResources resources = context.getResourceMocker();
+ resources.addOverride(R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT);
+ resources.addOverride(R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT);
+ resources.addOverride(R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
+ resources.addOverride(R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT);
+ resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
+ when(mDisplayPolicy.getSystemUiContext()).thenReturn(context);
+ when(mDisplayPolicy.hasNavigationBar()).thenReturn(true);
+
+ final int shortSizeDp =
+ Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
+ final int longSizeDp =
+ Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
+ mDisplayContent.getDisplayRotation().configure(
+ DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp, longSizeDp);
+ mDisplayPolicy.configure(DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp);
+ mDisplayPolicy.onConfigurationChanged();
+
+ mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
+ addWindow(mStatusBarWindow);
+ mDisplayPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT;
+
+ mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
+ addWindow(mNavBarWindow);
+ mDisplayPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
+ }
+
+ void addWindow(WindowState win) {
+ mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, true /* hasStatusBarPermission */);
+ assertEquals(WindowManagerGlobal.ADD_OKAY,
+ mDisplayPolicy.prepareAddWindowLw(win, win.mAttrs));
+ win.mHasSurface = true;
+ }
+
+ static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
+ boolean withDisplayCutout) {
+ final DisplayInfo info = new DisplayInfo();
+ WmDisplayCutout cutout = null;
+
+ final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
+ info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ info.rotation = rotation;
+ if (withDisplayCutout) {
+ cutout = WmDisplayCutout.computeSafeInsets(
+ displayCutoutForRotation(rotation), info.logicalWidth,
+ info.logicalHeight);
+ info.displayCutout = cutout.getDisplayCutout();
+ } else {
+ info.displayCutout = null;
+ }
+ return Pair.create(info, cutout);
+ }
+
+ private static DisplayCutout displayCutoutForRotation(int rotation) {
+ final RectF rectF =
+ new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
+
+ final Matrix m = new Matrix();
+ transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
+ m.mapRect(rectF);
+
+ int pos = -1;
+ switch (rotation) {
+ case ROTATION_0:
+ pos = BOUNDS_POSITION_TOP;
+ break;
+ case ROTATION_90:
+ pos = BOUNDS_POSITION_LEFT;
+ break;
+ case ROTATION_180:
+ pos = BOUNDS_POSITION_BOTTOM;
+ break;
+ case ROTATION_270:
+ pos = BOUNDS_POSITION_RIGHT;
+ break;
+ }
+
+ return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
+ (int) rectF.right, (int) rectF.bottom, pos);
+ }
+
+ static class TestContextWrapper extends ContextWrapper {
+ private final TestableResources mResourceMocker;
+
+ TestContextWrapper(Context targetContext) {
+ super(targetContext);
+ mResourceMocker = new TestableResources(targetContext.getResources());
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ public Resources getResources() {
+ return mResourceMocker.getResources();
+ }
+
+ TestableResources getResourceMocker() {
+ return mResourceMocker;
+ }
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index b823e70..7f390a5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -27,7 +27,12 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.WindowConfiguration;
import android.platform.test.annotations.Presubmit;
@@ -378,6 +383,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/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java
new file mode 100644
index 0000000..a04bf16
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.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.server.wm;
+
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+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 {
+
+ @Test
+ public void testIsDockSideAllowedDockTop() {
+ // Docked top is always allowed
+ assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT,
+ NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
+ assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT,
+ NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+ }
+
+ @Test
+ public void testIsDockSideAllowedDockBottom() {
+ // Cannot dock bottom
+ assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT,
+ NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
+ }
+
+ @Test
+ public void testIsDockSideAllowedNavigationBarMovable() {
+ assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
+ NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
+ assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
+ NAV_BAR_LEFT, true /* navigationBarCanMove */));
+ assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
+ NAV_BAR_RIGHT, true /* navigationBarCanMove */));
+ assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
+ NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
+ assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
+ NAV_BAR_RIGHT, true /* navigationBarCanMove */));
+ assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
+ NAV_BAR_LEFT, true /* navigationBarCanMove */));
+ }
+
+ @Test
+ public void testIsDockSideAllowedNavigationBarNotMovable() {
+ // Navigation bar is not movable such as tablets
+ assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
+ NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+ assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP,
+ NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+ assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT,
+ NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+ assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
+ NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+ assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP,
+ NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+ assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT,
+ NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
index 55e766d..ad293d9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
@@ -171,7 +171,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/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java
new file mode 100644
index 0000000..241b987
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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 org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsSourceProviderTest extends WindowTestsBase {
+
+ private InsetsSourceProvider mProvider = new InsetsSourceProvider(
+ new InsetsSource(TYPE_TOP_BAR));
+
+ @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/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java
new file mode 100644
index 0000000..7505db1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.platform.test.annotations.Presubmit;
+import android.view.InsetsState;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+@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");
+ mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+ .setWindow(topBar, null);
+ topBar.setInsetProvider(
+ mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR));
+ assertNotNull(mDisplayContent.getInsetsStateController().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(
+ mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR));
+ assertEquals(new InsetsState(),
+ mDisplayContent.getInsetsStateController().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");
+ mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+ .setWindow(topBar, null);
+ mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_NAVIGATION_BAR)
+ .setWindow(navBar, null);
+ mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_IME)
+ .setWindow(ime, null);
+ assertEquals(new InsetsState(),
+ mDisplayContent.getInsetsStateController().getInsetsForDispatch(navBar));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index fe5fc06..ee3bba7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -123,6 +123,8 @@
final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
hiddenAppWindow.setHidden(true);
+ mDisplayContent.getConfiguration().windowConfiguration.setRotation(
+ mDisplayContent.getRotation());
mController.initialize(mDisplayContent, ACTIVITY_TYPE_HOME, new SparseBooleanArray());
// Ensure that we are animating the target activity as well
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 6833dc5..e638a6a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -225,11 +225,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/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/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/servicestests/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/TaskWindowContainerControllerTests.java b/services/tests/servicestests/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/servicestests/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/servicestests/src/com/android/server/wm/TestIWindow.java
index 99deeb9..432af0d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -24,6 +24,7 @@
import android.view.DisplayCutout;
import android.view.DragEvent;
import android.view.IWindow;
+import android.view.InsetsState;
import com.android.internal.os.IResultReceiver;
@@ -39,6 +40,9 @@
Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException {
}
+ @Override
+ public void insetsChanged(InsetsState insetsState) 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/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 0165e7d..7b542cb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -26,12 +26,10 @@
import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.proto.ProtoOutputStream;
-import android.view.DisplayCutout;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.KeyEvent;
@@ -48,7 +46,6 @@
class TestWindowManagerPolicy implements WindowManagerPolicy {
private final Supplier<WindowManagerService> mWmSupplier;
- int rotationToReport = 0;
boolean keyguardShowingAndNotOccluded = false;
private Runnable mRunnableWhenAddingSplashScreen;
@@ -81,11 +78,6 @@
}
@Override
- public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
- boolean hasStatusBarServicePermission) {
- }
-
- @Override
public void adjustConfigurationLw(Configuration config, int keyboardPresence,
int navigationPresence) {
}
@@ -96,30 +88,6 @@
}
@Override
- public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
- int displayId, DisplayCutout displayCutout) {
- return 0;
- }
-
- @Override
- public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
- int displayId, DisplayCutout displayCutout) {
- return 0;
- }
-
- @Override
- public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
- int displayId, DisplayCutout displayCutout) {
- return 0;
- }
-
- @Override
- public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
- int displayId, DisplayCutout displayCutout) {
- return 0;
- }
-
- @Override
public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
return attrs.type == TYPE_STATUS_BAR;
}
@@ -166,28 +134,7 @@
}
@Override
- public int prepareAddWindowLw(WindowState win,
- WindowManager.LayoutParams attrs) {
- return 0;
- }
-
- @Override
- public void removeWindowLw(WindowState win) {
- }
-
- @Override
- public int selectAnimationLw(WindowState win, int transit) {
- return 0;
- }
-
- @Override
- public void selectRotationAnimationLw(int[] anim) {
- }
-
- @Override
- public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId,
- boolean forceDefault) {
- return false;
+ public void setKeyguardCandidateLw(WindowState win) {
}
@Override
@@ -222,32 +169,11 @@
}
@Override
- public int getSystemDecorLayerLw() {
- return 0;
+ public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) {
}
@Override
- public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
- }
-
- @Override
- public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
- WindowState attached, WindowState imeTarget) {
- }
-
- @Override
- public int finishPostLayoutPolicyLw() {
- return 0;
- }
-
- @Override
- public boolean allowAppAnimationsLw() {
- return false;
- }
-
- @Override
- public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
- return 0;
+ public void setAllowLockscreenWhenOn(int displayId, boolean allow) {
}
@Override
@@ -349,11 +275,6 @@
}
@Override
- public boolean isShowingDreamLw() {
- return false;
- }
-
- @Override
public void onKeyguardOccludedChangedLw(boolean occluded) {
}
@@ -399,11 +320,6 @@
}
@Override
- public int adjustSystemUiVisibilityLw(int visibility) {
- return 0;
- }
-
- @Override
public boolean hasNavigationBar() {
return false;
}
@@ -421,6 +337,16 @@
}
@Override
+ public boolean isUserSetupComplete() {
+ return false;
+ }
+
+ @Override
+ public int getUiMode() {
+ return 0;
+ }
+
+ @Override
public void setCurrentUserLw(int newUserId) {
}
@@ -446,43 +372,6 @@
}
@Override
- public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
- DisplayCutout cutout, Rect outInsets) {
- }
-
- @Override
- public boolean isNavBarForcedShownLw(WindowState win) {
- return false;
- }
-
- @NavigationBarPosition
- @Override
- public int getNavBarPosition() {
- return NAV_BAR_BOTTOM;
- }
-
- @Override
- public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
- DisplayCutout cutout, Rect outInsets) {
- }
-
- @Override
- public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
- int displayHeight, int displayRotation) {
- return false;
- }
-
- @Override
- public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
- }
-
- @Override
- public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation,
- int newRotation) {
- return false;
- }
-
- @Override
public void setPipVisibilityLw(boolean visible) {
}
@@ -508,10 +397,6 @@
}
@Override
- public void onLockTaskStateChangedLw(int lockTaskState) {
- }
-
- @Override
public boolean setAodShowing(boolean aodShowing) {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
index 25e73e3..4ea6b39 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -48,7 +48,7 @@
synchronized (mWm.mGlobalLock) {
// No wallpaper
final DisplayContent dc = createNewDisplay();
- Bitmap wallpaperBitmap = mWm.mRoot.mWallpaperController.screenshotWallpaperLocked();
+ Bitmap wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
assertNull(wallpaperBitmap);
// No wallpaper WSA Surface
@@ -56,25 +56,25 @@
true, dc, true /* ownerCanManageAppTokens */);
WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
wallpaperWindowToken, "wallpaperWindow");
- wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+ wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
assertNull(wallpaperBitmap);
// Wallpaper with not visible WSA surface.
wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
wallpaperWindow.mWinAnimator.mLastAlpha = 1;
- wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+ wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
assertNull(wallpaperBitmap);
when(windowSurfaceController.getShown()).thenReturn(true);
// Wallpaper with WSA alpha set to 0.
wallpaperWindow.mWinAnimator.mLastAlpha = 0;
- wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+ wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
assertNull(wallpaperBitmap);
// Wallpaper window with WSA Surface
wallpaperWindow.mWinAnimator.mLastAlpha = 1;
- wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+ wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
assertNotNull(wallpaperBitmap);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index b0c8d8b..227eb00 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -113,9 +113,6 @@
@Before
public void setUp() throws Exception {
- // Just any non zero value.
- mWm.mSystemDecorLayer = 10000;
-
mWindowToken = WindowTestUtils.createTestAppWindowToken(
mWm.getDefaultDisplayContentLocked());
mStubStack = new TaskStack(mWm, 0, null);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 80bb936..2e47c35 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -18,7 +18,6 @@
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;
@@ -27,21 +26,16 @@
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
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;
@@ -54,37 +48,6 @@
public class WindowTestUtils {
private static int sNextTaskId = 0;
- /** An extension of {@link DisplayContent} to gain package scoped access. */
- public static class TestDisplayContent extends DisplayContent {
-
- private TestDisplayContent(Display display, WindowManagerService service,
- WallpaperController wallpaperController, DisplayWindowController controller) {
- super(display, service, wallpaperController, controller);
- }
-
- /** Create a mocked default {@link DisplayContent}. */
- public static TestDisplayContent create(Context context) {
- final TestDisplayContent displayContent = mock(TestDisplayContent.class);
- displayContent.isDefaultDisplay = true;
-
- final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
- when(displayPolicy.navigationBarCanMove()).thenReturn(true);
- when(displayPolicy.hasNavigationBar()).thenReturn(true);
-
- final DisplayRotation displayRotation = new DisplayRotation(
- mock(WindowManagerService.class), displayContent, displayPolicy,
- context, new Object());
- displayRotation.mPortraitRotation = Surface.ROTATION_0;
- displayRotation.mLandscapeRotation = Surface.ROTATION_90;
- displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
- displayRotation.mSeascapeRotation = Surface.ROTATION_270;
-
- when(displayContent.getDisplayRotation()).thenReturn(displayRotation);
-
- return displayContent;
- }
- }
-
/**
* Creates a mock instance of {@link StackWindowController}.
*/
@@ -152,12 +115,11 @@
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) {
+ boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) {
super(service, token, activityComponent, voiceInteraction, dc,
inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
orientation, rotationAnimationHint, configChanges, launchTaskBehind,
- alwaysFocusable, controller);
+ alwaysFocusable, activityRecord);
}
int getWindowsCount() {
@@ -311,45 +273,6 @@
}
}
- 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();
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 53858c7..1eb46fb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -90,7 +90,6 @@
WindowState mChildAppWindowAbove;
WindowState mChildAppWindowBelow;
HashSet<WindowState> mCommonWindows;
- WallpaperController mWallpaperController;
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -119,8 +118,6 @@
mWm = mWmRule.getWindowManagerService();
beforeCreateDisplay();
- mWallpaperController = new WallpaperController(mWm);
-
context.getDisplay().getDisplayInfo(mDisplayInfo);
mDisplayContent = createNewDisplay();
mWm.mDisplayEnabled = true;
@@ -363,8 +360,7 @@
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
synchronized (mWm.mGlobalLock) {
- return new DisplayContent(display, mWm, mWallpaperController,
- mock(DisplayWindowController.class));
+ return new DisplayContent(display, mWm, mock(DisplayWindowController.class));
}
}
diff --git a/services/tests/servicestests/test-apps/Android.mk b/services/tests/servicestests/test-apps/Android.mk
deleted file mode 100644
index 5053e7d..0000000
--- a/services/tests/servicestests/test-apps/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-subdir-makefiles)
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
new file mode 100644
index 0000000..13e6644
--- /dev/null
+++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
@@ -0,0 +1,31 @@
+// 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.
+
+android_test_helper_app {
+ name: "ConnTestApp",
+
+ test_suites: ["device-tests"],
+
+ static_libs: ["servicestests-aidl"],
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
deleted file mode 100644
index 18b8c2d..0000000
--- a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
+++ /dev/null
@@ -1,32 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := servicestests-aidl
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := ConnTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
new file mode 100644
index 0000000..ae1eca7
--- /dev/null
+++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+android_test_helper_app {
+ name: "JobTestApp",
+
+ sdk_version: "current",
+
+ test_suites: ["device-tests"],
+
+ srcs: ["**/*.java"],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.mk b/services/tests/servicestests/test-apps/JobTestApp/Android.mk
deleted file mode 100644
index 7893c91..0000000
--- a/services/tests/servicestests/test-apps/JobTestApp/Android.mk
+++ /dev/null
@@ -1,30 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := JobTestApp
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
new file mode 100644
index 0000000..7257275
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+android_test_helper_app {
+ name: "SuspendTestApp",
+
+ test_suites: ["device-tests"],
+
+ static_libs: [
+ "androidx.test.runner",
+ "ub-uiautomator",
+ ],
+
+ srcs: [
+ "**/*.java",
+ ":servicestests-SuspendTestApp-files",
+ ],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+
+ platform_apis: true,
+
+}
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
deleted file mode 100644
index ab222b9..0000000
--- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
+++ /dev/null
@@ -1,34 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.runner ub-uiautomator
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java
-
-LOCAL_PACKAGE_NAME := SuspendTestApp
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp
new file mode 100644
index 0000000..c2cb6881
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/Android.bp
@@ -0,0 +1,26 @@
+// 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.
+
+java_library {
+ name: "ShortcutManagerTestUtils",
+
+ srcs: ["src/**/*.java"],
+
+ libs: [
+ "mockito-target",
+ "android.test.runner.stubs",
+ ],
+
+ sdk_version: "test_current",
+}
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
deleted file mode 100644
index 019bcbd3..0000000
--- a/services/tests/shortcutmanagerutils/Android.mk
+++ /dev/null
@@ -1,32 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := \
- mockito-target \
- android.test.runner.stubs
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := ShortcutManagerTestUtils
-
-LOCAL_SDK_VERSION := test_current
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
new file mode 100644
index 0000000..ca8cc0d
--- /dev/null
+++ b/services/tests/uiservicestests/Android.bp
@@ -0,0 +1,58 @@
+//########################################################################
+// Build FrameworksUiServicesTests package
+//########################################################################
+
+android_test {
+ name: "FrameworksUiServicesTests",
+
+ // Include test java files
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "services.accessibility",
+ "services.core",
+ "services.devicepolicy",
+ "services.net",
+ "services.usage",
+ "guava",
+ "android-support-test",
+ "mockito-target-inline-minus-junit4",
+ "platform-test-annotations",
+ "testables",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ dxflags: ["--multi-dex"],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+
+ compile_multilib: "both",
+
+ // These are not normally accessible from apps so they must be explicitly included.
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libmultiplejvmtiagentsinterferenceagent",
+ "libbacktrace",
+ "libbase",
+ "libbinder",
+ "libbinderthreadstate",
+ "libc++",
+ "libcutils",
+ "liblog",
+ "liblzma",
+ "libnativehelper",
+ "libnetdaidl",
+ "libui",
+ "libunwindstack",
+ "libutils",
+ ],
+}
diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
deleted file mode 100644
index f3f4355..0000000
--- a/services/tests/uiservicestests/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-#########################################################################
-# Build FrameworksUiServicesTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include test java files and source from notifications package.
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
- $(call all-java-files-under, ../../core/java/com/android/server/notification) \
- $(call all-java-files-under, ../../core/java/com/android/server/slice) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- services.accessibility \
- services.core \
- services.devicepolicy \
- services.net \
- services.usage \
- guava \
- android-support-test \
- mockito-target-inline-minus-junit4 \
- platform-test-annotations \
- testables
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PACKAGE_NAME := FrameworksUiServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_MULTILIB := both
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- libdexmakerjvmtiagent \
- libmultiplejvmtiagentsinterferenceagent \
- libbacktrace \
- libbase \
- libbinder \
- libbinderthreadstate \
- libc++ \
- libcutils \
- liblog \
- liblzma \
- libnativehelper \
- libnetdaidl \
- libui \
- libunwindstack \
- libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-include $(BUILD_PACKAGE)
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 3266b8b..d950360 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -38,10 +38,8 @@
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Build.VERSION_CODES.P;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -68,16 +66,15 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.Application;
import android.app.IActivityManager;
import android.app.INotificationManager;
+import android.app.ITransientNotification;
+import android.app.IUriGrantsManager;
import android.app.Notification;
import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
-import android.app.ITransientNotification;
-import android.app.IUriGrantsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
@@ -100,7 +97,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings.Secure;
import android.service.notification.Adjustment;
@@ -116,7 +112,6 @@
import android.text.Html;
import android.util.ArrayMap;
import android.util.AtomicFile;
-import android.util.Log;
import com.android.internal.R;
import com.android.internal.statusbar.NotificationVisibility;
@@ -288,6 +283,7 @@
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
+ when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
// write to a test file; the system file isn't readable from tests
mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -1735,7 +1731,8 @@
}
@Test
- public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+ public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess()
+ throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
@@ -2509,6 +2506,7 @@
mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
+ verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.sbn));
}
@Test
@@ -2517,8 +2515,11 @@
mService.addNotification(r);
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((true)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false);
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((false)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
}
@@ -2529,8 +2530,12 @@
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(false), eq((true)));
+
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(
+ eq(r.sbn), eq(false), eq((false)));
}
@Test
@@ -3459,11 +3464,12 @@
ApplicationInfo info = new ApplicationInfo();
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
- assertTrue(mService.isCallerInstantApp("any", 45770, 0));
+ assertTrue(mService.isCallerInstantApp(45770, 0));
info.privateFlags = 0;
- assertFalse(mService.isCallerInstantApp("any", 575370, 0));
+ assertFalse(mService.isCallerInstantApp(575370, 0));
}
@Test
@@ -3472,8 +3478,9 @@
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
- assertTrue(mService.isCallerInstantApp("any", 68638450, 10));
+ assertTrue(mService.isCallerInstantApp(68638450, 10));
}
@Test
@@ -3689,4 +3696,38 @@
new TestableToastCallback(), 2000, 0);
assertEquals(1, mService.mToastQueue.size());
}
+
+ @Test
+ public void testOnNotificationSmartReplySent() {
+ final int replyIndex = 2;
+ final String reply = "Hello";
+ final boolean generatedByAssistant = true;
+
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationSmartReplySent(
+ r.getKey(), replyIndex, reply, generatedByAssistant);
+ 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/Android.bp b/services/tests/wmtests/Android.bp
new file mode 100644
index 0000000..cdba9a1
--- /dev/null
+++ b/services/tests/wmtests/Android.bp
@@ -0,0 +1,52 @@
+//########################################################################
+// Build WmTests package
+//########################################################################
+
+android_test {
+ name: "WmTests",
+
+ // We only want this apk build for tests.
+
+ // Include all test java files.
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "frameworks-base-testutils",
+ "services.core",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ "servicestests-utils",
+ "truth-prebuilt",
+ "testables",
+ "ub-uiautomator",
+ "hamcrest-library",
+ ],
+
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ // These are not normally accessible from apps so they must be explicitly included.
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+
+ dxflags: ["--multi-dex"],
+
+ optimize: {
+ enabled: false,
+ },
+
+}
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
deleted file mode 100644
index 67c2860..0000000
--- a/services/tests/wmtests/Android.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-#########################################################################
-# Build WmTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, ../servicestests/utils) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- frameworks-base-testutils \
- services.core \
- androidx.test.runner \
- androidx.test.rules \
- mockito-target-extended-minus-junit4 \
- platform-test-annotations \
- truth-prebuilt \
- testables \
- ub-uiautomator \
- hamcrest-library
-
-LOCAL_JAVA_LIBRARIES := \
- android.test.mock \
- android.test.base \
- android.test.runner \
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- libdexmakerjvmtiagent \
- libstaticjvmtiagent \
-
-LOCAL_PACKAGE_NAME := WmTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f128b4e2..67acec6 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -37,6 +37,8 @@
<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/policy/FakeWindowState.java b/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
deleted file mode 100644
index d4f2b06..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
+++ /dev/null
@@ -1,260 +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.policy;
-
-import android.graphics.Rect;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-import android.view.IApplicationToken;
-import android.view.WindowManager;
-
-import com.android.server.wm.WindowFrames;
-
-public class FakeWindowState implements WindowManagerPolicy.WindowState {
-
- private WindowFrames mWindowFrames = new WindowFrames();
-
- public WindowManager.LayoutParams attrs;
- public int displayId;
- public boolean isVoiceInteraction;
- public boolean inMultiWindowMode;
- public boolean visible = true;
- public int surfaceLayer = 1;
- public boolean isDimming = false;
-
- public boolean policyVisible = true;
-
- @Override
- public int getOwningUid() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public String getOwningPackage() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public void computeFrameLw() {
- }
-
- @Override
- public Rect getFrameLw() {
- return mWindowFrames.mParentFrame;
- }
-
- @Override
- public Rect getDisplayFrameLw() {
- return mWindowFrames.mDisplayFrame;
- }
-
- @Override
- public Rect getOverscanFrameLw() {
- return mWindowFrames.mOverscanFrame;
- }
-
- @Override
- public Rect getContentFrameLw() {
- return mWindowFrames.mContentFrame;
- }
-
- @Override
- public Rect getVisibleFrameLw() {
- return mWindowFrames.mVisibleFrame;
- }
-
- public Rect getStableFrame() {
- return mWindowFrames.mStableFrame;
- }
-
- public Rect getDecorFrame() {
- return mWindowFrames.mDecorFrame;
- }
-
- @Override
- public boolean getGivenInsetsPendingLw() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public Rect getGivenContentInsetsLw() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public Rect getGivenVisibleInsetsLw() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public WindowManager.LayoutParams getAttrs() {
- return attrs;
- }
-
- @Override
- public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public int getSystemUiVisibility() {
- return attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility;
- }
-
- @Override
- public int getSurfaceLayer() {
- return surfaceLayer;
- }
-
- @Override
- public int getBaseType() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public IApplicationToken getAppToken() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public boolean isVoiceInteraction() {
- return isVoiceInteraction;
- }
-
- @Override
- public boolean hasAppShownWindows() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public boolean isVisibleLw() {
- return visible && policyVisible;
- }
-
- @Override
- public boolean isDisplayedLw() {
- return isVisibleLw();
- }
-
- @Override
- public boolean isAnimatingLw() {
- return false;
- }
-
- @Override
- public boolean canAffectSystemUiFlags() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public boolean isGoneForLayoutLw() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public boolean isDrawnLw() {
- return true;
- }
-
- @Override
- public boolean hasDrawnLw() {
- return true;
- }
-
- @Override
- public boolean hideLw(boolean doAnimation) {
- if (!policyVisible) {
- return false;
- }
- policyVisible = false;
- return true;
- }
-
- @Override
- public boolean showLw(boolean doAnimation) {
- if (policyVisible) {
- return false;
- }
- policyVisible = true;
- return true;
- }
-
- @Override
- public boolean isAlive() {
- return true;
- }
-
- @Override
- public boolean isDefaultDisplay() {
- return displayId == Display.DEFAULT_DISPLAY;
- }
-
- @Override
- public boolean isDimming() {
- return isDimming;
- }
-
- @Override
- public int getWindowingMode() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public boolean isInMultiWindowMode() {
- return inMultiWindowMode;
- }
-
- @Override
- public int getRotationAnimationHint() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public boolean isInputMethodWindow() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public int getDisplayId() {
- return displayId;
- }
-
- @Override
- public boolean canAcquireSleepToken() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public boolean canReceiveKeys() {
- return false;
- }
-
- @Override
- public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
- throw new UnsupportedOperationException("not implemented");
- }
-
- @Override
- public WindowFrames getWindowFrames() {
- return mWindowFrames;
- }
-
- @Override
- public boolean isInputMethodTarget() {
- return false;
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
deleted file mode 100644
index e8f767a..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ /dev/null
@@ -1,395 +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.policy;
-
-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.PRIVATE_FLAG_IS_SCREEN_DECOR;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.view.DisplayCutout;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Build/Install/Run:
- * atest WmTests:PhoneWindowManagerLayoutTest
- */
-@SmallTest
-@Presubmit
-public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
-
- private FakeWindowState mAppWindow;
-
- @Before
- public void setUp() throws Exception {
- mAppWindow = new FakeWindowState();
- mAppWindow.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
- TYPE_APPLICATION,
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- PixelFormat.TRANSLUCENT);
-
- addStatusBar();
- addNavigationBar();
- }
-
- @Test
- public void layoutWindowLw_appDrawsBars() {
- mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
- assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
- assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
-
- @Test
- public void layoutWindowLw_appWontDrawBars() {
- mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
- }
-
- @Test
- public void layoutWindowLw_appWontDrawBars_forceStatus() {
- mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
- }
-
- @Test
- public void addingWindow_doesNotTamperWithSysuiFlags() {
- mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mPolicy.addWindow(mAppWindow);
-
- assertEquals(0, mAppWindow.attrs.systemUiVisibility);
- assertEquals(0, mAppWindow.attrs.subtreeSystemUiVisibility);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout() {
- addDisplayCutout();
-
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
- assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
- }
-
- @Test
- public void layoutWindowLw_withhDisplayCutout_never() {
- addDisplayCutout();
-
- mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
- addDisplayCutout();
-
- mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
- assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
- assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreen() {
- addDisplayCutout();
-
- mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
- addDisplayCutout();
-
- mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
- assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
- }
-
-
- @Test
- public void layoutWindowLw_withDisplayCutout_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mAppWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_seascape() {
- addDisplayCutout();
- setRotation(ROTATION_270);
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetBy(mAppWindow.getFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mAppWindow.getStableFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
- assertInsetBy(mAppWindow.getContentFrameLw(),
- NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
-
- mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
- addDisplayCutout();
-
- mAppWindow.attrs.flags = FLAG_LAYOUT_IN_SCREEN;
- mAppWindow.attrs.type = TYPE_APPLICATION_OVERLAY;
- mAppWindow.attrs.width = DISPLAY_WIDTH;
- mAppWindow.attrs.height = DISPLAY_HEIGHT;
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
-
- mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mPolicy.addWindow(mAppWindow);
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
- assertInsetBy(mAppWindow.getFrameLw(), 0, 0, 0, 0);
- assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
- }
-
- @Test
- public void layoutHint_screenDecorWindow() {
- addDisplayCutout();
- mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
-
- mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
- final Rect frame = new Rect();
- final Rect content = new Rect();
- final Rect stable = new Rect();
- final Rect outsets = new Rect();
- final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper();
- mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames,
- false /* floatingStack */, frame, content, stable, outsets, cutout);
-
- assertThat(frame, equalTo(mFrames.mUnrestricted));
- assertThat(content, equalTo(new Rect()));
- assertThat(stable, equalTo(new Rect()));
- assertThat(outsets, equalTo(new Rect()));
- assertThat(cutout.get(), equalTo(DisplayCutout.NO_CUTOUT));
- }
-
- @Test
- public void layoutHint_appWindow() {
- // Initialize DisplayFrames
- mPolicy.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();
-
- mPolicy.getLayoutHintLw(mAppWindow.attrs, 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
- mPolicy.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();
-
- mPolicy.getLayoutHintLw(mAppWindow.attrs, 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
- mPolicy.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();
-
- mPolicy.getLayoutHintLw(mAppWindow.attrs, 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()));
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
deleted file mode 100644
index 6c44d65..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
+++ /dev/null
@@ -1,255 +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.policy;
-
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-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.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.graphics.PixelFormat;
-import android.platform.test.annotations.Presubmit;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Build/Install/Run:
- * atest WmTests:PhoneWindowManagerTest
- */
-@SmallTest
-@Presubmit
-public class PhoneWindowManagerTest {
-
- private static FakeWindowState createOpaqueFullscreen(boolean hasLightNavBar) {
- final FakeWindowState state = new FakeWindowState();
- state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
- TYPE_BASE_APPLICATION,
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- PixelFormat.OPAQUE);
- state.attrs.subtreeSystemUiVisibility =
- hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
- return state;
- }
-
- private static FakeWindowState createDimmingDialogWindow(boolean canBeImTarget) {
- final FakeWindowState state = new FakeWindowState();
- state.attrs = new WindowManager.LayoutParams(WRAP_CONTENT, WRAP_CONTENT,
- TYPE_APPLICATION,
- FLAG_DIM_BEHIND | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM),
- PixelFormat.TRANSLUCENT);
- state.isDimming = true;
- return state;
- }
-
- private static FakeWindowState createInputMethodWindow(boolean visible, boolean drawNavBar,
- boolean hasLightNavBar) {
- final FakeWindowState state = new FakeWindowState();
- state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
- TYPE_INPUT_METHOD,
- FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN
- | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0),
- PixelFormat.TRANSPARENT);
- state.attrs.subtreeSystemUiVisibility =
- hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
- state.visible = visible;
- state.policyVisible = visible;
- return state;
- }
-
-
- @Test
- public void testChooseNavigationColorWindowLw() {
- final FakeWindowState opaque = createOpaqueFullscreen(false);
-
- final FakeWindowState dimmingImTarget = createDimmingDialogWindow(true);
- final FakeWindowState dimmingNonImTarget = createDimmingDialogWindow(false);
-
- final FakeWindowState visibleIme = createInputMethodWindow(true, true, false);
- final FakeWindowState invisibleIme = createInputMethodWindow(false, true, false);
- final FakeWindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
-
- // If everything is null, return null
- assertNull(null, PhoneWindowManager.chooseNavigationColorWindowLw(
- null, null, null, NAV_BAR_BOTTOM));
-
- assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, opaque, null, NAV_BAR_BOTTOM));
- assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, dimmingImTarget, null, NAV_BAR_BOTTOM));
- assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM));
-
- assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
- null, null, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
- null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
- null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
-
- assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
- assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
- assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, opaque, visibleIme, NAV_BAR_RIGHT));
-
- // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
- // window.
- assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM));
- assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
- assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
- opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
- }
-
- @Test
- public void testUpdateLightNavigationBarLw() {
- final FakeWindowState opaqueDarkNavBar = createOpaqueFullscreen(false);
- final FakeWindowState opaqueLightNavBar = createOpaqueFullscreen(true);
-
- final FakeWindowState dimming = createDimmingDialogWindow(false);
-
- final FakeWindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false);
- final FakeWindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true);
-
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- PhoneWindowManager.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null,
- null, null));
-
- // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag.
- assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
- 0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
- assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null,
- opaqueDarkNavBar));
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- PhoneWindowManager.updateLightNavigationBarLw(0, opaqueLightNavBar,
- opaqueLightNavBar, null, opaqueLightNavBar));
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- PhoneWindowManager.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
-
- // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
- assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
- 0, opaqueDarkNavBar, dimming, null, dimming));
- assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
- 0, opaqueLightNavBar, dimming, null, dimming));
- assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming));
- assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming));
- assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar,
- dimming));
-
- // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
- assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar,
- imeDrawDarkNavBar));
-
- // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins.
- assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar,
- imeDrawDarkNavBar, imeDrawDarkNavBar));
-
- // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- PhoneWindowManager.updateLightNavigationBarLw(0, opaqueDarkNavBar,
- opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
- }
-
- @Test
- public void testIsDockSideAllowedDockTop() {
- // Docked top is always allowed
- assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
- true /* navigationBarCanMove */));
- assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
- false /* navigationBarCanMove */));
- }
-
- @Test
- public void testIsDockSideAllowedDockBottom() {
- // Cannot dock bottom
- assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, NAV_BAR_BOTTOM,
- true /* navigationBarCanMove */));
- }
-
- @Test
- public void testIsDockSideAllowedNavigationBarMovable() {
- assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
- true /* navigationBarCanMove */));
- assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_LEFT,
- true /* navigationBarCanMove */));
- assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_RIGHT,
- true /* navigationBarCanMove */));
- assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
- true /* navigationBarCanMove */));
- assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_RIGHT,
- true /* navigationBarCanMove */));
- assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_LEFT,
- true /* navigationBarCanMove */));
- }
-
- @Test
- public void testIsDockSideAllowedNavigationBarNotMovable() {
- // Navigation bar is not movable such as tablets
- assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
- false /* navigationBarCanMove */));
- assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, NAV_BAR_BOTTOM,
- false /* navigationBarCanMove */));
- assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
- false /* navigationBarCanMove */));
- assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
- false /* navigationBarCanMove */));
- assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, NAV_BAR_BOTTOM,
- false /* navigationBarCanMove */));
- assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
- false /* navigationBarCanMove */));
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
deleted file mode 100644
index 02a33e0..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ /dev/null
@@ -1,278 +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.policy;
-
-import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
-import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.server.wm.utils.CoordinateTransforms
- .transformPhysicalToLogicalCoordinates;
-
-import static org.junit.Assert.assertEquals;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Matrix;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.os.IBinder;
-import android.os.UserHandle;
-import android.testing.TestableResources;
-import android.util.Pair;
-import android.view.Display;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
-import android.view.Gravity;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IAccessibilityManager;
-
-import com.android.server.policy.keyguard.KeyguardServiceDelegate;
-import com.android.server.wm.DisplayFrames;
-import com.android.server.wm.WindowTestUtils.TestDisplayContent;
-import com.android.server.wm.utils.WmDisplayCutout;
-
-import org.junit.Before;
-
-class PhoneWindowManagerTestBase {
- static final int DISPLAY_WIDTH = 500;
- static final int DISPLAY_HEIGHT = 1000;
-
- static final int STATUS_BAR_HEIGHT = 10;
- static final int NAV_BAR_HEIGHT = 15;
- static final int DISPLAY_CUTOUT_HEIGHT = 8;
-
- TestablePhoneWindowManager mPolicy;
- TestContextWrapper mContext;
- DisplayFrames mFrames;
-
- FakeWindowState mStatusBar;
- FakeWindowState mNavigationBar;
- private boolean mHasDisplayCutout;
- private int mRotation = ROTATION_0;
-
- @Before
- public void setUpBase() {
- mContext = new TestContextWrapper(getInstrumentation().getTargetContext());
- mContext.getResourceMocker().addOverride(
- com.android.internal.R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT);
- mContext.getResourceMocker().addOverride(
- com.android.internal.R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT);
- mContext.getResourceMocker().addOverride(
- com.android.internal.R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
- mContext.getResourceMocker().addOverride(
- com.android.internal.R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT);
- mContext.getResourceMocker().addOverride(
- com.android.internal.R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
-
- mPolicy = TestablePhoneWindowManager.create(mContext);
-
- updateDisplayFrames();
- }
-
- public void setRotation(int rotation) {
- mRotation = rotation;
- updateDisplayFrames();
- }
-
- private void updateDisplayFrames() {
- Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
- mHasDisplayCutout);
- mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info.first, info.second);
- }
-
- public void addStatusBar() {
- mStatusBar = new FakeWindowState();
- mStatusBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, STATUS_BAR_HEIGHT,
- TYPE_STATUS_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT);
- mStatusBar.attrs.gravity = Gravity.TOP;
-
- mPolicy.addWindow(mStatusBar);
- mPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT;
- }
-
- public void addNavigationBar() {
- mNavigationBar = new FakeWindowState();
- mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, NAV_BAR_HEIGHT,
- TYPE_NAVIGATION_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT);
- mNavigationBar.attrs.gravity = Gravity.BOTTOM;
-
- mPolicy.addWindow(mNavigationBar);
- mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
- }
-
- public void addDisplayCutout() {
- mHasDisplayCutout = true;
- updateDisplayFrames();
- }
-
- /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
- public void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
- int expectedInsetRight, int expectedInsetBottom) {
- assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
- mFrames.mDisplayWidth - expectedInsetRight,
- mFrames.mDisplayHeight - expectedInsetBottom), actual);
- }
-
- /**
- * 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.
- */
- public void assertInsetByTopBottom(Rect actual, int expectedInsetTop, int expectedInsetBottom) {
- assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
- }
-
- public static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
- return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first;
- }
- public static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
- boolean withDisplayCutout) {
- DisplayInfo info = new DisplayInfo();
- WmDisplayCutout cutout = null;
-
- final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
- info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
- info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
- info.rotation = rotation;
- if (withDisplayCutout) {
- cutout = WmDisplayCutout.computeSafeInsets(
- displayCutoutForRotation(rotation), info.logicalWidth,
- info.logicalHeight);
- info.displayCutout = cutout.getDisplayCutout();
- } else {
- info.displayCutout = null;
- }
- return Pair.create(info, cutout);
- }
-
- private static DisplayCutout displayCutoutForRotation(int rotation) {
- RectF rectF = new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
-
- Matrix m = new Matrix();
- transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
- m.mapRect(rectF);
-
- int pos = -1;
- switch (rotation) {
- case ROTATION_0:
- pos = BOUNDS_POSITION_TOP;
- break;
- case ROTATION_90:
- pos = BOUNDS_POSITION_LEFT;
- break;
- case ROTATION_180:
- pos = BOUNDS_POSITION_BOTTOM;
- break;
- case ROTATION_270:
- pos = BOUNDS_POSITION_RIGHT;
- break;
- }
-
-
- return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
- (int) rectF.right, (int) rectF.bottom, pos);
- }
-
- static class TestContextWrapper extends ContextWrapper {
- private final TestableResources mResourceMocker;
-
- TestContextWrapper(Context targetContext) {
- super(targetContext);
- mResourceMocker = new TestableResources(targetContext.getResources());
- }
-
- @Override
- public int checkPermission(String permission, int pid, int uid) {
- return PackageManager.PERMISSION_GRANTED;
- }
-
- @Override
- public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
- return PackageManager.PERMISSION_GRANTED;
- }
-
- @Override
- public Resources getResources() {
- return mResourceMocker.getResources();
- }
-
- public TestableResources getResourceMocker() {
- return mResourceMocker;
- }
- }
-
- static class TestablePhoneWindowManager extends PhoneWindowManager {
-
- TestablePhoneWindowManager() {
- }
-
- @Override
- void initializeHdmiState() {
- // Do nothing.
- }
-
- @Override
- Context getSystemUiContext() {
- return mContext;
- }
-
- void addWindow(WindowState state) {
- if (state instanceof FakeWindowState) {
- ((FakeWindowState) state).surfaceLayer =
- getWindowLayerFromTypeLw(state.getAttrs().type,
- true /* canAddInternalSystemWindow */);
- }
- adjustWindowParamsLw(state, state.getAttrs(), true /* hasStatusBarPermission */);
- assertEquals(WindowManagerGlobal.ADD_OKAY, prepareAddWindowLw(state, state.getAttrs()));
- }
-
- public static TestablePhoneWindowManager create(Context context) {
- TestablePhoneWindowManager[] policy = new TestablePhoneWindowManager[1];
- getInstrumentation().runOnMainSync(() -> {
- policy[0] = new TestablePhoneWindowManager();
- policy[0].mContext = context;
- policy[0].mKeyguardDelegate = mock(KeyguardServiceDelegate.class);
- policy[0].mAccessibilityManager = new AccessibilityManager(context,
- mock(IAccessibilityManager.class), UserHandle.USER_CURRENT);
- policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class);
-
- final TestDisplayContent displayContent = TestDisplayContent.create(context);
- policy[0].setDefaultDisplay(displayContent);
- policy[0].onConfigurationChanged(displayContent);
- });
- return policy[0];
- }
- }
-}
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..0e30037 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -74,7 +74,7 @@
// 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();
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 6ed83de..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.
@@ -163,7 +162,8 @@
private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds,
float aspectRatio, Rect expectedActivityBounds) {
// Verify with nav bar on the right.
- when(mService.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
+ when(mService.mWindowManager.getNavBarPosition(mActivity.getDisplayId()))
+ .thenReturn(navBarPosition);
mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
mActivity.info.maxAspectRatio = aspectRatio;
mActivity.ensureActivityConfiguration(
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..78a67d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -38,8 +38,7 @@
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.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
import static com.google.common.truth.Truth.assertThat;
@@ -83,78 +82,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 +111,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);
@@ -198,334 +130,4 @@
assertEquals(deliverToTopWait.who, firstActivity.realActivity);
}
}
-
- @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..0da0b24 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();
@@ -239,8 +239,8 @@
.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..d78fbcd 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,7 @@
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;
@Before
public void setUp() throws Exception {
@@ -125,7 +128,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 +139,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);
@@ -314,7 +317,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 +326,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 +346,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());
}
@@ -441,7 +444,7 @@
final ActivityStack focusStack = focusActivity.getStack();
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();
@@ -473,7 +476,7 @@
// Enter split-screen. Primary stack should have focus.
focusActivity.getStack().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 +489,7 @@
*/
@Test
public void testTaskModeViolation() {
- final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
((TestActivityDisplay) display).removeAllTasks();
assertNoTasks(display);
@@ -551,6 +554,85 @@
}
/**
+ * 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, 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, 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, false, false);
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted",
+ false, Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1, false, false);
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_hasVisibleWindow_notAborted",
+ false, UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1, false, false);
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_processStateTop_notAborted",
+ false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP, false, false);
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_hasForegroundActivities_notAborted",
+ false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, true, false);
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_callerIsRecents_notAborted",
+ false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false, true);
+ }
+
+ private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
+ int testCallingUid, boolean hasVisibleWindow, int procState,
+ boolean hasForegroundActivities, boolean callerIsRecents) {
+ // window visibility
+ doReturn(hasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
+ testCallingUid);
+ // process importance
+ doReturn(procState).when(mService).getUidStateLocked(testCallingUid);
+ // foreground activities
+ final IApplicationThread caller = mock(IApplicationThread.class);
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.uid = testCallingUid;
+ final WindowProcessController callerApp =
+ new WindowProcessController(mService, ai, null, testCallingUid, -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(testCallingUid);
+
+ final ActivityOptions options = spy(ActivityOptions.makeBasic());
+ ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK).setCaller(caller)
+ .setCallingUid(testCallingUid).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 +644,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 +682,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 caabdbd..ead9731 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -35,6 +35,7 @@
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;
@@ -66,6 +67,8 @@
import com.android.server.AttributeCache;
import com.android.server.ServiceThread;
import com.android.server.am.ActivityManagerService;
+import com.android.server.am.PendingIntentController;
+import com.android.server.firewall.IntentFirewall;
import com.android.server.uri.UriGrantsManagerInternal;
import org.junit.After;
@@ -91,6 +94,7 @@
final TestInjector mTestInjector = new TestInjector();
ActivityTaskManagerService mService;
+ RootActivityContainer mRootActivityContainer;
ActivityStackSupervisor mSupervisor;
// Default package name
@@ -115,27 +119,14 @@
}
ActivityTaskManagerService createActivityTaskManagerService() {
- final TestActivityTaskManagerService atm =
- spy(new TestActivityTaskManagerService(mContext));
- setupActivityManagerService(atm);
- return atm;
+ mService = new TestActivityTaskManagerService(mContext);
+ mSupervisor = mService.mStackSupervisor;
+ mRootActivityContainer = mService.mRootActivityContainer;
+ return mService;
}
void setupActivityTaskManagerService() {
- mService = createActivityTaskManagerService();
- mSupervisor = mService.mStackSupervisor;
- }
-
- ActivityManagerService createActivityManagerService() {
- final TestActivityTaskManagerService atm =
- spy(new TestActivityTaskManagerService(mContext));
- return setupActivityManagerService(atm);
- }
-
- ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
- final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
- setupActivityManagerService(am, atm);
- return am;
+ createActivityTaskManagerService();
}
/** Creates a {@link TestActivityDisplay}. */
@@ -150,36 +141,10 @@
/** 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;
}
- void setupActivityManagerService(
- TestActivityManagerService am, TestActivityTaskManagerService atm) {
- atm.setActivityManagerService(am.mIntentFirewall, am.mPendingIntentController);
- atm.mAmInternal = am.getLocalService();
- am.mAtmInternal = atm.getLocalService();
- // Makes sure the supervisor is using with the spy object.
- atm.mStackSupervisor.setService(atm);
- doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
- doReturn(mock(IPackageManager.class)).when(atm).getPackageManager();
- PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
- doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
- doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
- doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
- am.mActivityTaskManager = atm;
- am.mWindowManager = prepareMockWindowManager();
- atm.setWindowManager(am.mWindowManager);
-
- // Put a home stack on the default display, so that we'll always have something focusable.
- final TestActivityStackSupervisor supervisor =
- (TestActivityStackSupervisor) atm.mStackSupervisor;
- supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
- .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
- new ActivityBuilder(atm).setTask(task).build();
- }
-
/**
* Builder for creating new activities.
*/
@@ -268,7 +233,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);
@@ -354,7 +321,7 @@
TaskRecord build() {
if (mStack == null && mCreateStack) {
- mStack = mSupervisor.getDefaultDisplay().createStack(
+ mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
}
@@ -405,23 +372,72 @@
}
}
- protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
- private LockTaskController mLockTaskController;
- private ActivityTaskManagerInternal mInternal;
+ protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
private PackageManagerInternal mPmInternal;
// ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
// We keep the reference in order to prevent creating it twice.
- private ActivityStackSupervisor mTestStackSupervisor;
+ ActivityStackSupervisor mTestStackSupervisor;
+
+ ActivityDisplay mDefaultDisplay;
TestActivityTaskManagerService(Context context) {
super(context);
+ spyOn(this);
+
+ mUgmInternal = mock(UriGrantsManagerInternal.class);
+
mSupportsMultiWindow = true;
mSupportsMultiDisplay = true;
mSupportsSplitScreenMultiWindow = true;
mSupportsFreeformWindowManagement = true;
mSupportsPictureInPicture = true;
- mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+ final TestActivityManagerService am =
+ new TestActivityManagerService(mTestInjector, this);
+
+ 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) {
+ mAmInternal = amInternal;
+ setActivityManagerService(intentFirewall, intentController);
+ 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
@@ -430,54 +446,17 @@
}
@Override
- public LockTaskController getLockTaskController() {
- if (mLockTaskController == null) {
- mLockTaskController = spy(super.getLockTaskController());
- }
-
- return mLockTaskController;
- }
-
- @Override
void updateUsageStats(ActivityRecord component, boolean resumed) {
}
@Override
- protected final ActivityStackSupervisor createStackSupervisor() {
+ protected ActivityStackSupervisor createStackSupervisor() {
if (mTestStackSupervisor == null) {
- final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
- final KeyguardController keyguardController = mock(KeyguardController.class);
-
- // Invoked during {@link ActivityStack} creation.
- doNothing().when(supervisor).updateUIDsPresentOnDisplay();
- // Always keep things awake.
- doReturn(true).when(supervisor).hasAwakeDisplay();
- // Called when moving activity to pinned stack.
- doNothing().when(supervisor).ensureActivitiesVisibleLocked(any(), anyInt(),
- anyBoolean());
- // Do not schedule idle timeouts
- doNothing().when(supervisor).scheduleIdleTimeoutLocked(any());
- // unit test version does not handle launch wake lock
- doNothing().when(supervisor).acquireLaunchWakelock();
- doReturn(keyguardController).when(supervisor).getKeyguardController();
-
- supervisor.initialize();
- mTestStackSupervisor = supervisor;
+ mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
}
return mTestStackSupervisor;
}
- protected ActivityStackSupervisor createTestSupervisor() {
- return new TestActivityStackSupervisor(this, mH.getLooper());
- }
-
- ActivityTaskManagerInternal getLocalService() {
- if (mInternal == null) {
- mInternal = new ActivityTaskManagerService.LocalService();
- }
- return mInternal;
- }
-
@Override
PackageManagerInternal getPackageManagerInternalLocked() {
if (mPmInternal == null) {
@@ -524,24 +503,31 @@
}
}
+ // TODO: Replace this with a mock object since we are no longer in AMS package.
/**
* An {@link ActivityManagerService} subclass which provides a test
* {@link ActivityStackSupervisor}.
*/
- static class TestActivityManagerService extends ActivityManagerService {
+ class TestActivityManagerService extends ActivityManagerService {
- private ActivityManagerInternal mInternal;
-
- TestActivityManagerService(TestInjector testInjector) {
+ TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) {
super(testInjector, testInjector.mHandlerThread);
- mUgmInternal = mock(UriGrantsManagerInternal.class);
- }
+ spyOn(this);
- ActivityManagerInternal getLocalService() {
- if (mInternal == null) {
- mInternal = new LocalService();
- }
- return mInternal;
+ mWindowManager = prepareMockWindowManager();
+ mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+ atm.setActivityManagerService(mIntentFirewall, mPendingIntentController,
+ new LocalService(), mWindowManager);
+
+ mActivityTaskManager = atm;
+ mAtmInternal = atm.mInternal;
+
+ doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+ PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
+ doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked();
+ doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
+ doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
}
}
@@ -549,24 +535,22 @@
* An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
* setup not available in the test environment. Also specifies an injector for
*/
- protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
- private ActivityDisplay mDisplay;
+ protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
private KeyguardController mKeyguardController;
- public TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+ TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
super(service, looper);
- mDisplayManager =
- (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
+ spyOn(this);
mWindowManager = prepareMockWindowManager();
mKeyguardController = mock(KeyguardController.class);
- setWindowContainerController(mock(RootWindowContainerController.class));
- }
- @Override
- public void initialize() {
- super.initialize();
- mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
- addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+ // Do not schedule idle timeouts
+ doNothing().when(this).scheduleIdleTimeoutLocked(any());
+ // unit test version does not handle launch wake lock
+ doNothing().when(this).acquireLaunchWakelock();
+ doReturn(mKeyguardController).when(this).getKeyguardController();
+
+ initialize();
}
@Override
@@ -575,11 +559,6 @@
}
@Override
- ActivityDisplay getDefaultDisplay() {
- return mDisplay;
- }
-
- @Override
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
}
@@ -596,7 +575,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);
@@ -604,7 +583,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);
@@ -615,7 +594,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();
}
@@ -757,8 +736,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;
@@ -766,9 +745,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) {
@@ -805,7 +784,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);
@@ -821,7 +801,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/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/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/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 2f3f698..3c7b4b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -110,14 +110,13 @@
@Before
public void setUp() throws Exception {
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
- mTestService = spy(new MyTestActivityTaskManagerService(mContext));
- final TestActivityManagerService am = spy(new MyTestActivityManagerService());
- setupActivityManagerService(am, mTestService);
+ 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);
@@ -868,20 +867,20 @@
}
@Override
- protected ActivityStackSupervisor createTestSupervisor() {
- return new MyTestActivityStackSupervisor(this, mH.getLooper());
- }
-
- }
-
- private class MyTestActivityManagerService extends TestActivityManagerService {
- MyTestActivityManagerService() {
- super(mTestInjector);
+ protected ActivityStackSupervisor createStackSupervisor() {
+ if (mTestStackSupervisor == null) {
+ mTestStackSupervisor = new MyTestActivityStackSupervisor(this, mH.getLooper());
+ }
+ return mTestStackSupervisor;
}
@Override
- public boolean isUserRunning(int userId, int flags) {
- return true;
+ 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);
}
}
@@ -891,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/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 50190e7..0ff67d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -25,6 +25,7 @@
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.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
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.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
@@ -50,61 +51,50 @@
public class RecentsAnimationTest extends ActivityTestsBase {
private Context mContext = InstrumentationRegistry.getContext();
- private TestActivityTaskManagerService mTestService;
private ComponentName mRecentsComponent;
@Before
public void setUp() throws Exception {
mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
- mTestService = spy(new MyTestActivityTaskManagerService(mContext));
- setupActivityManagerService(mTestService);
+ mService = new TestActivityTaskManagerService(mContext);
+
+ final RecentTasks recentTasks = mService.getRecentTasks();
+ spyOn(recentTasks);
+ mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
+ doReturn(mRecentsComponent).when(recentTasks).getRecentsComponent();
}
@Test
public void testCancelAnimationOnStackOrderChange() {
ActivityStack fullscreenStack =
- mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- ActivityStack recentsStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ ActivityStack recentsStack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
- ActivityRecord recentsActivity = new ActivityBuilder(mTestService)
+ ActivityRecord recentsActivity = new ActivityBuilder(mService)
.setComponent(mRecentsComponent)
.setCreateTask(true)
.setStack(recentsStack)
.build();
ActivityStack fullscreenStack2 =
- mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- ActivityRecord fsActivity = new ActivityBuilder(mTestService)
+ ActivityRecord fsActivity = new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "App1"))
.setCreateTask(true)
.setStack(fullscreenStack2)
.build();
- doReturn(true).when(mTestService.mWindowManager).canStartRecentsAnimation();
+ doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
// Start the recents animation
Intent recentsIntent = new Intent();
recentsIntent.setComponent(mRecentsComponent);
- mTestService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
+ mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
fullscreenStack.moveToFront("Activity start");
// Ensure that the recents animation was canceled
- verify(mTestService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+ verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
eq(REORDER_KEEP_IN_PLACE), any());
}
-
- private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
- MyTestActivityTaskManagerService(Context context) {
- super(context);
- }
-
- @Override
- protected RecentTasks createRecentTasks() {
- RecentTasks recents = mock(RecentTasks.class);
- doReturn(mRecentsComponent).when(recents).getRecentsComponent();
- System.out.println(mRecentsComponent);
- return recents;
- }
- }
}
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..631de99d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.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 com.android.server.wm;
+
+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;
+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.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;
+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.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();
+ 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.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 = 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/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 6638eeb..bd8cd1f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -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/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 72d7c90..630a8bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -47,12 +47,10 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
+import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
@@ -79,15 +77,10 @@
final TaskRecord expected = createTaskRecord(64);
expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
- final File serializedFile = serializeToFile(expected);
-
- try {
- final TaskRecord actual = restoreFromFile(serializedFile);
- assertEquals(expected.taskId, actual.taskId);
- assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
- } finally {
- serializedFile.delete();
- }
+ final byte[] serializedBytes = serializeToBytes(expected);
+ final TaskRecord actual = restoreFromBytes(serializedBytes);
+ assertEquals(expected.taskId, actual.taskId);
+ assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
}
@Test
@@ -131,10 +124,8 @@
assertTrue(task.returnsToHomeStack());
}
- private File serializeToFile(TaskRecord r) throws IOException, XmlPullParserException {
- final File tmpFile = File.createTempFile(r.taskId + "_task_", "xml");
-
- try (OutputStream os = new FileOutputStream(tmpFile)) {
+ private byte[] serializeToBytes(TaskRecord r) throws IOException, XmlPullParserException {
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
final XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(os, "UTF-8");
serializer.startDocument(null, true);
@@ -142,13 +133,14 @@
r.saveToXml(serializer);
serializer.endTag(null, TASK_TAG);
serializer.endDocument();
- }
- return tmpFile;
+ os.flush();
+ return os.toByteArray();
+ }
}
- private TaskRecord restoreFromFile(File file) throws IOException, XmlPullParserException {
- try (Reader reader = new BufferedReader(new FileReader(file))) {
+ private TaskRecord restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
+ try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(reader);
assertEquals(XmlPullParser.START_TAG, parser.next());
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 cf34fe7..3c87721 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -42,8 +42,8 @@
public static class TestDisplayContent extends DisplayContent {
private TestDisplayContent(Display display, WindowManagerService service,
- WallpaperController wallpaperController, DisplayWindowController controller) {
- super(display, service, wallpaperController, controller);
+ DisplayWindowController controller) {
+ super(display, service, controller);
}
/**
@@ -65,7 +65,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;
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 152831f..4f573a4 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -27,8 +27,6 @@
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
@@ -846,8 +844,6 @@
// Inform listeners if necessary
if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
|| event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
- || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START
- || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_STOP
|| event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
|| event.mEventType == UsageEvents.Event.USER_INTERACTION
|| event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
@@ -900,10 +896,6 @@
switch (eventType) {
case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
- case UsageEvents.Event.FOREGROUND_SERVICE_START:
- return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
- case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
- return REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index d940620..01e566c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -20,6 +20,7 @@
import android.app.usage.UsageStats;
import android.content.res.Configuration;
import android.util.ArrayMap;
+import android.util.Log;
import com.android.internal.util.XmlUtils;
@@ -89,11 +90,23 @@
// Apply the offset to the beginTime to find the absolute time.
stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
parser, LAST_TIME_ACTIVE_ATTR);
- stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
- parser, LAST_TIME_SERVICE_USED_ATTR);
+
+ try {
+ stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
+ parser, LAST_TIME_SERVICE_USED_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e);
+ }
+
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
- stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
+
+ try {
+ stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
TOTAL_TIME_SERVICE_USED_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
+ }
+
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
stats.mAppLaunchCount = XmlUtils.readIntAttribute(parser, APP_LAUNCH_COUNT_ATTR,
0);
@@ -350,8 +363,17 @@
}
statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
- statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
- statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
+ try {
+ statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse majorVersion", e);
+ }
+
+ try {
+ statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse minorVersion", e);
+ }
int eventCode;
int outerDepth = parser.getDepth();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index bcc0e6b..185c886 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1023,9 +1023,9 @@
public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
/**
- * Default Enhanced 4G LTE mode enabled. When this is {@code true}, Enhanced 4G LTE mode by
- * default is on, otherwise if {@code false}, Enhanced 4G LTE mode by default is off.
- * @hide
+ * Sets the default state for the "Enhanced 4G LTE" or "Advanced Calling" mode toggle set by the
+ * user. When this is {@code true}, this mode by default is on, otherwise if {@code false},
+ * this mode by default is off.
*/
public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL =
"enhanced_4g_lte_on_by_default_bool";
@@ -1838,6 +1838,13 @@
"notify_international_call_on_wfc_bool";
/**
+ * Flag to hide Preset APN details. If true, user cannot enter ApnEditor view of Preset APN,
+ * and cannot view details of the APN. If false, user can enter ApnEditor view of Preset APN.
+ * Default value is false.
+ */
+ public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
+
+ /**
* Flag specifying whether to show an alert dialog for video call charges.
* By default this value is {@code false}.
* @hide
@@ -2643,6 +2650,7 @@
sDefaults.putBoolean(KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL,
false);
sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
+ sDefaults.putBoolean(KEY_HIDE_PRESET_APN_DETAILS_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
null);
diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
index e373797..269cda1 100644
--- a/telephony/java/android/telephony/MbmsGroupCallSession.java
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -37,6 +37,7 @@
import android.util.ArraySet;
import android.util.Log;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -107,14 +108,14 @@
* {@link MbmsGroupCallSession} that you received before calling this method again.
*
* @param context The {@link Context} to use.
- * @param executor The executor on which you wish to execute callbacks.
* @param subscriptionId The subscription ID to use.
+ * @param executor The executor on which you wish to execute callbacks.
* @param callback A callback object on which you wish to receive results of asynchronous
* operations.
* @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred.
*/
public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
- @NonNull Executor executor, int subscriptionId,
+ int subscriptionId, @NonNull Executor executor,
final @NonNull MbmsGroupCallSessionCallback callback) {
if (!sIsInitialized.compareAndSet(false, true)) {
throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
@@ -138,11 +139,11 @@
/**
* Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
- * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ * See {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
*/
public static MbmsGroupCallSession create(@NonNull Context context,
@NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
- return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+ return create(context, SubscriptionManager.getDefaultSubscriptionId(), executor, callback);
}
/**
@@ -153,7 +154,7 @@
* instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been
* enqueued will still be delivered.
*
- * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to
+ * It is safe to call {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)} to
* obtain another instance of {@link MbmsGroupCallSession} immediately after this method
* returns.
*
@@ -189,18 +190,19 @@
* Asynchronous errors through the callback include any of the errors in
* {@link MbmsErrors.GeneralErrors}.
*
- * @param executor The executor on which you wish to execute callbacks for this stream.
* @param tmgi The TMGI, an identifier for the group call you want to join.
- * @param saiArray An array of SAIs for the group call that should be negotiated separately with
+ * @param saiList A list of SAIs for the group call that should be negotiated separately with
* the carrier.
- * @param frequencyArray An array of frequencies for the group call that should be negotiated
+ * @param frequencyList A lost of frequencies for the group call that should be negotiated
* separately with the carrier.
+ * @param executor The executor on which you wish to execute callbacks for this stream.
* @param callback The callback that you want to receive information about the call on.
* @return An instance of {@link GroupCall} through which the call can be controlled.
* May be {@code null} if an error occurred.
*/
- public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray,
- int[] frequencyArray, @NonNull GroupCallCallback callback) {
+ public @Nullable GroupCall startGroupCall(long tmgi, @NonNull List<Integer> saiList,
+ @NonNull List<Integer> frequencyList, @NonNull Executor executor,
+ @NonNull GroupCallCallback callback) {
IMbmsGroupCallService groupCallService = mService.get();
if (groupCallService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -215,7 +217,7 @@
try {
int returnCode = groupCallService.startGroupCall(
- mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback);
+ mSubscriptionId, tmgi, saiList, frequencyList, serviceCallback);
if (returnCode == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
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/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 3200aea..45cfe1e 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
@@ -568,14 +569,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.
@@ -1426,7 +1419,7 @@
* subscriptions in the slot.
*/
@Nullable
- public static int[] getSubscriptionIds(int slotIndex) {
+ public int[] getSubscriptionIds(int slotIndex) {
return getSubId(slotIndex);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8324f00..79ed93e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7696,7 +7696,7 @@
try {
return getITelephony().isAvailable(getSubId(),
MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
- ImsRegistrationImplBase.REGISTRATION_TECH_LTE, getOpPackageName());
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
} catch (RemoteException | NullPointerException ex) {
return false;
}
@@ -8557,6 +8557,26 @@
return UNKNOWN_CARRIER_ID;
}
+ /**
+ * Returns carrier id based on MCCMNC only. This is for fallback when exact carrier id
+ * {@link #getSimCarrierId()} configurations are not found
+ *
+ * @return matching carrier id from passing mccmnc.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getCarrierIdFromMccMnc(String mccmnc) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getCarrierIdFromMccMnc(getSlotIndex(), mccmnc);
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ return UNKNOWN_CARRIER_ID;
+ }
+
/**
* 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
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index bdba8c8..2d46ec2 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -17,8 +17,8 @@
package android.telephony.emergency;
import android.annotation.IntDef;
-import android.hardware.radio.V1_3.EmergencyNumberSource;
-import android.hardware.radio.V1_3.EmergencyServiceCategory;
+import android.hardware.radio.V1_4.EmergencyNumberSource;
+import android.hardware.radio.V1_4.EmergencyServiceCategory;
import android.os.Parcel;
import android.os.Parcelable;
@@ -138,7 +138,7 @@
}
/**
- * The source to tell where the corresponding @1.3::EmergencyNumber comes from.
+ * The source to tell where the corresponding @1.4::EmergencyNumber comes from.
*
* The emergency number has one or more defined emergency number sources.
*
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index e06c372..122626f 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -28,17 +28,22 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telephony.AccessNetworkConstants;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.ITelephony;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.Executor;
/**
@@ -52,6 +57,7 @@
* @see #createForSubscriptionId(Context, int)
* @hide
*/
+@SystemApi
public class ImsMmTelManager {
private static final String TAG = "ImsMmTelManager";
@@ -70,16 +76,12 @@
/**
* Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
* registration if signal quality degrades.
- * @hide
*/
- @SystemApi
public static final int WIFI_MODE_WIFI_ONLY = 0;
/**
* Prefer registering for IMS over LTE if LTE signal quality is high enough.
- * @hide
*/
- @SystemApi
public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
/**
@@ -91,13 +93,26 @@
/**
* Callback class for receiving Registration callback events.
- * @see #addImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
- * @see #removeImsRegistrationCallback(RegistrationCallback)
+ * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
*/
public static class RegistrationCallback {
private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
+ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
+ // and WWAN are more accurate constants.
+ private static final Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP =
+ new HashMap<Integer, Integer>() {{
+ // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
+ // case, since it is defined.
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+ AccessNetworkConstants.TransportType.WWAN);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+ AccessNetworkConstants.TransportType.WLAN);
+ }};
+
private final RegistrationCallback mLocalCallback;
private Executor mExecutor;
@@ -109,16 +124,16 @@
public void onRegistered(int imsRadioTech) {
if (mLocalCallback == null) return;
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> mLocalCallback.onRegistered(imsRadioTech)));
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+ mLocalCallback.onRegistered(getAccessType(imsRadioTech))));
}
@Override
public void onRegistering(int imsRadioTech) {
if (mLocalCallback == null) return;
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> mLocalCallback.onRegistering(imsRadioTech)));
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+ mLocalCallback.onRegistering(getAccessType(imsRadioTech))));
}
@Override
@@ -134,8 +149,8 @@
if (mLocalCallback == null) return;
Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() ->
- mLocalCallback.onTechnologyChangeFailed(imsRadioTech, info)));
+ mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
+ getAccessType(imsRadioTech), info)));
}
@Override
@@ -150,6 +165,15 @@
private void setExecutor(Executor executor) {
mExecutor = executor;
}
+
+ private static int getAccessType(int regType) {
+ if (!IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
+ Log.w("ImsMmTelManager", "RegistrationBinder - invalid regType returned: "
+ + regType);
+ return -1;
+ }
+ return IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
+ }
}
private final RegistrationBinder mBinder = new RegistrationBinder(this);
@@ -157,19 +181,19 @@
/**
* Notifies the framework when the IMS Provider is registered to the IMS network.
*
- * @param imsRadioTech the radio access technology. Valid values are defined in
- * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+ * @param imsTransportType the radio access technology. Valid values are defined in
+ * {@link android.telephony.AccessNetworkConstants.TransportType}.
*/
- public void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ public void onRegistered(int imsTransportType) {
}
/**
* Notifies the framework when the IMS Provider is trying to register the IMS network.
*
- * @param imsRadioTech the radio access technology. Valid values are defined in
- * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+ * @param imsTransportType the radio access technology. Valid values are defined in
+ * {@link android.telephony.AccessNetworkConstants.TransportType}.
*/
- public void onRegistering(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ public void onRegistering(int imsTransportType) {
}
/**
@@ -182,14 +206,14 @@
/**
* A failure has occurred when trying to handover registration to another technology type,
- * defined in {@link ImsRegistrationImplBase.ImsRegistrationTech}
+ * defined in {@link android.telephony.AccessNetworkConstants.TransportType}
*
- * @param imsRadioTech The {@link ImsRegistrationImplBase.ImsRegistrationTech} type that has
- * failed
+ * @param imsTransportType The
+ * {@link android.telephony.AccessNetworkConstants.TransportType}
+ * transport type that has failed to handover registration to.
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
- public void onTechnologyChangeFailed(
- @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) {
+ public void onTechnologyChangeFailed(int imsTransportType, ImsReasonInfo info) {
}
/**
@@ -219,8 +243,8 @@
/**
* Receives IMS capability status updates from the ImsService.
*
- * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
- * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+ * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
+ * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
*/
public static class CapabilityCallback {
@@ -285,13 +309,12 @@
}
}
- private Context mContext;
private int mSubId;
/**
* Create an instance of ImsManager for the subscription id specified.
*
- * @param context
+ * @param context The context to create this ImsMmTelManager instance within.
* @param subId The ID of the subscription that this ImsMmTelManager will use.
* @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
* @throws IllegalArgumentException if the subscription is invalid or
@@ -303,11 +326,15 @@
throw new IllegalArgumentException("Invalid subscription ID");
}
- return new ImsMmTelManager(context, subId);
+ return new ImsMmTelManager(subId);
}
- private ImsMmTelManager(Context context, int subId) {
- mContext = context;
+ /**
+ * Only visible for testing, use {@link #createForSubscriptionId(Context, int)} instead.
+ * @hide
+ */
+ @VisibleForTesting
+ public ImsMmTelManager(int subId) {
mSubId = subId;
}
@@ -315,14 +342,18 @@
* Registers a {@link RegistrationCallback} with the system, which will provide registration
* updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use
* {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
- * events and call {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up
+ * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up
* after a subscription is removed.
+ *
+ * When the callback is registered, it will initiate the callback c to be called with the
+ * current registration state.
+ *
* @param executor The executor the callback events should be run on.
* @param c The {@link RegistrationCallback} to be added.
- * @see #removeImsRegistrationCallback(RegistrationCallback)
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
- public void addImsRegistrationCallback(@CallbackExecutor Executor executor,
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerImsRegistrationCallback(@CallbackExecutor Executor executor,
@NonNull RegistrationCallback c) {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -332,8 +363,7 @@
}
c.setExecutor(executor);
try {
- getITelephony().addImsRegistrationCallback(mSubId, c.getBinder(),
- mContext.getOpPackageName());
+ getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -344,16 +374,15 @@
* up to avoid memory leaks or when the subscription is removed.
* @param c The {@link RegistrationCallback} to be removed.
* @see SubscriptionManager.OnSubscriptionsChangedListener
- * @see #addImsRegistrationCallback(Executor, RegistrationCallback)
+ * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
- public void removeImsRegistrationCallback(@NonNull RegistrationCallback c) {
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
}
try {
- getITelephony().removeImsRegistrationCallback(mSubId, c.getBinder(),
- mContext.getOpPackageName());
+ getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -364,14 +393,18 @@
* updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}.
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
* subscription changed events and call
- * {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up after a subscription
- * is removed.
+ * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up after a
+ * subscription is removed.
+ *
+ * When the callback is registered, it will initiate the callback c to be called with the
+ * current capabilities.
+ *
* @param executor The executor the callback events should be run on.
* @param c The MmTel {@link CapabilityCallback} to be registered.
- * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+ * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
- public void addMmTelCapabilityCallback(@CallbackExecutor Executor executor,
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerMmTelCapabilityCallback(@CallbackExecutor Executor executor,
@NonNull CapabilityCallback c) {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -381,8 +414,7 @@
}
c.setExecutor(executor);
try {
- getITelephony().addMmTelCapabilityCallback(mSubId, c.getBinder(),
- mContext.getOpPackageName());
+ getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -392,29 +424,42 @@
* Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning
* up to avoid memory leaks.
* @param c The MmTel {@link CapabilityCallback} to be removed.
- * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback)
+ * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
- public void removeMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
}
try {
- getITelephony().removeMmTelCapabilityCallback(mSubId, c.getBinder(),
- mContext.getOpPackageName());
+ getITelephony().unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/**
- * Query the user's setting for whether or not to use MmTel capabilities over IMS,
- * such as voice and video, depending on carrier configuration for the current subscription.
+ * Query the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+ * enable MmTel IMS features, depending on the carrier configuration for the current
+ * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+ * be enabled as long as the carrier has provisioned these services for the specified
+ * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+ * carrier requirements.
+ *
+ * Modifying this value may also trigger an IMS registration or deregistration, depending on
+ * whether or not the new value is enabled or disabled.
+ *
+ * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+ * method will do nothing and will instead always use the default value.
+ *
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
* @see #setAdvancedCallingSetting(boolean)
- * @return true if the user’s setting for advanced calling is enabled and false otherwise.
- * @hide
+ * @return true if the user's setting for advanced calling is enabled, false otherwise.
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isAdvancedCallingSettingEnabled() {
try {
@@ -426,13 +471,25 @@
/**
* Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
- * enable MmTel IMS features, such as voice and video calling, depending on the carrier
- * configuration for the current subscription. Modifying this value may also trigger an IMS
- * registration or deregistration, depending on the new value.
- * @see #isAdvancedCallingEnabled()
- * @hide
+ * enable MmTel IMS features, depending on the carrier configuration for the current
+ * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+ * be enabled as long as the carrier has provisioned these services for the specified
+ * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+ * carrier requirements.
+ *
+ * Modifying this value may also trigger an IMS registration or deregistration, depending on
+ * whether or not the new value is enabled or disabled.
+ *
+ * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+ * method will do nothing and will instead always use the default value.
+ *
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+ * @see #isAdvancedCallingSettingEnabled()
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setAdvancedCallingSetting(boolean isEnabled) {
try {
@@ -464,12 +521,11 @@
* @return {@code true} if the MmTel IMS capability is capable for this subscription, false
* otherwise.
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
try {
- return getITelephony().isCapable(mSubId, capability, imsRegTech,
- mContext.getOpPackageName());
+ return getITelephony().isCapable(mSubId, capability, imsRegTech);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -492,12 +548,11 @@
* @return {@code true} if the MmTel IMS capability is available for this subscription, false
* otherwise.
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
try {
- return getITelephony().isAvailable(mSubId, capability, imsRegTech,
- mContext.getOpPackageName());
+ return getITelephony().isAvailable(mSubId, capability, imsRegTech);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -508,11 +563,10 @@
* @return true if the user’s “Video Calling” setting is currently enabled.
* @see #setVtSetting(boolean)
*/
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isVtSettingEnabled() {
try {
- return getITelephony().isVtSettingEnabled(mSubId, mContext.getOpPackageName());
+ return getITelephony().isVtSettingEnabled(mSubId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -521,9 +575,7 @@
/**
* Change the user's setting for Video Telephony and enable the Video Telephony capability.
* @see #isVtSettingEnabled()
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVtSetting(boolean isEnabled) {
try {
@@ -537,9 +589,7 @@
/**
* @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
* @see #setVoWiFiSetting(boolean)
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isVoWiFiSettingEnabled() {
try {
@@ -553,9 +603,7 @@
* Sets the user's setting for whether or not Voice over WiFi is enabled.
* @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
* @see #isVoWiFiSettingEnabled()
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiSetting(boolean isEnabled) {
try {
@@ -570,9 +618,7 @@
* @return true if the user's setting for Voice over WiFi while roaming is enabled, false
* if disabled.
* @see #setVoWiFiRoamingSetting(boolean)
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isVoWiFiRoamingSettingEnabled() {
try {
@@ -587,9 +633,7 @@
* @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
* false otherwise.
* @see #isVoWiFiRoamingSettingEnabled()
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiRoamingSetting(boolean isEnabled) {
try {
@@ -611,9 +655,7 @@
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #setVoWiFiSetting(boolean)
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
try {
@@ -631,9 +673,7 @@
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #setVoWiFiSetting(boolean)
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @WiFiCallingMode int getVoWiFiModeSetting() {
try {
@@ -651,9 +691,7 @@
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #getVoWiFiModeSetting()
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
try {
@@ -674,9 +712,7 @@
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #setVoWiFiRoamingSetting(boolean)
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@WiFiCallingMode int getVoWiFiRoamingModeSetting() {
try {
@@ -696,9 +732,7 @@
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #getVoWiFiRoamingModeSetting()
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
try {
@@ -712,9 +746,7 @@
/**
* Change the user's setting for RTT capability of this device.
* @param isEnabled if true RTT will be enabled during calls.
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRttCapabilitySetting(boolean isEnabled) {
try {
@@ -729,9 +761,7 @@
* @return true if TTY over VoLTE is supported
* @see android.telecom.TelecomManager#getCurrentTtyMode
* @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
boolean isTtyOverVolteEnabled() {
try {
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 3f22f98..b55866b 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -425,6 +425,12 @@
*/
public final void addCapabilityCallback(IImsCapabilityCallback c) {
mCapabilityCallbacks.register(c);
+ try {
+ // Notify the Capability callback that was just registered of the current capabilities.
+ c.onCapabilitiesStatusChanged(queryCapabilityStatus().mCapabilities);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "addCapabilityCallback: error accessing callback: " + e.getMessage());
+ }
}
/**
diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java
index 9aca18e..25e274e 100644
--- a/telephony/java/android/telephony/mbms/GroupCall.java
+++ b/telephony/java/android/telephony/mbms/GroupCall.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.RemoteException;
import android.telephony.MbmsGroupCallSession;
import android.telephony.mbms.vendor.IMbmsGroupCallService;
@@ -24,6 +25,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/**
* Class used to represent a single MBMS group call. After a call has been started with
@@ -41,8 +43,26 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
public @interface GroupCallState {}
+
+ /**
+ * Indicates that the group call is in a stopped state
+ *
+ * This can be reported after network action or after calling {@link #close}.
+ */
public static final int STATE_STOPPED = 1;
+
+ /**
+ * Indicates that the group call is started.
+ *
+ * Data can be transmitted and received in this state.
+ */
public static final int STATE_STARTED = 2;
+
+ /**
+ * Indicates that the group call is stalled.
+ *
+ * This may be due to a network issue or the device being temporarily out of range.
+ */
public static final int STATE_STALLED = 3;
/**
@@ -122,16 +142,17 @@
* Send an update to the middleware when the SAI (Service Area Identifier) list and frequency
* information of the group call has * changed. Callers must obtain this information from the
* wireless carrier independently.
- * @param saiArray New array of SAIs that the call is available on.
- * @param frequencyArray New array of frequencies that the call is available on.
+ * @param saiList New list of SAIs that the call is available on.
+ * @param frequencyList New list of frequencies that the call is available on.
*/
- public void updateGroupCall(int[] saiArray, int[] frequencyArray) {
+ public void updateGroupCall(@NonNull List<Integer> saiList,
+ @NonNull List<Integer> frequencyList) {
if (mService == null) {
throw new IllegalStateException("No group call service attached");
}
try {
- mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray);
+ mService.updateGroupCall(mSubscriptionId, mTmgi, saiList, frequencyList);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService = null;
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 001bb02..77e36bb 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.Nullable;
import java.lang.annotation.Retention;
@@ -26,7 +27,7 @@
* A callback class for use when the application is in a group call. The middleware
* will provide updates on the status of the call via this callback.
*/
-public class GroupCallCallback {
+public interface GroupCallCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
@@ -40,7 +41,7 @@
MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
- private @interface GroupCallError{}
+ @interface GroupCallError{}
/**
* Indicates broadcast signal strength is not available for this call.
@@ -48,7 +49,7 @@
* This may be due to the call no longer being available due to geography
* or timing (end of service)
*/
- public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+ int SIGNAL_STRENGTH_UNAVAILABLE = -1;
/**
* Called by the middleware when it has detected an error condition in this group call. The
@@ -56,9 +57,7 @@
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- public void onError(@GroupCallError int errorCode, @Nullable String message) {
- // default implementation empty
- }
+ void onError(@GroupCallError int errorCode, @Nullable String message);
/**
* Called to indicate this call has changed state.
@@ -66,10 +65,8 @@
* See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
* and {@link GroupCall#STATE_STALLED}.
*/
- public void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
- @GroupCall.GroupCallStateChangeReason int reason) {
- // default implementation empty
- }
+ void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+ @GroupCall.GroupCallStateChangeReason int reason);
/**
* Broadcast Signal Strength updated.
@@ -81,7 +78,5 @@
* {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
* for this call due to timing, geography or popularity.
*/
- public void onBroadcastSignalStrengthUpdated(int signalStrength) {
- // default implementation empty
- }
+ void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength);
}
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index 7c4321b..52e4d33 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -140,5 +140,21 @@
public static final int ERROR_UNKNOWN_FILE_INFO = 403;
}
+ /**
+ * Indicates the errors that are applicable only to the group call use-case.
+ */
+ public static class GroupCallErrors {
+ private GroupCallErrors() { }
+ /** Indicates that the middleware was unable to start the group call. */
+ public static final int ERROR_UNABLE_TO_START_SERVICE = 501;
+
+ /**
+ * Indicates that the app called
+ * {@link android.telephony.MbmsGroupCallSession#startGroupCall} more than once for the
+ * same {@code tmgi}.
+ */
+ public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502;
+ }
+
private MbmsErrors() {}
}
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
index 7da734e..04e7ba1 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.telephony.MbmsGroupCallSession;
@@ -29,9 +30,9 @@
/**
* A callback class that is used to receive information from the middleware on MBMS group-call
* services. An instance of this object should be passed into
- * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ * {@link MbmsGroupCallSession#create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
*/
-public class MbmsGroupCallSessionCallback {
+public interface MbmsGroupCallSessionCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
@@ -48,7 +49,7 @@
MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
- private @interface GroupCallError{}
+ @interface GroupCallError{}
/**
* Called by the middleware when it has detected an error condition. The possible error codes
@@ -56,8 +57,7 @@
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- public void onError(@GroupCallError int errorCode, @Nullable String message) {
- }
+ void onError(@GroupCallError int errorCode, @Nullable String message);
/**
* Indicates that the list of currently available SAIs has been updated. The app may use this
@@ -70,21 +70,22 @@
* @param availableSais A list of lists of available SAIS in neighboring cells, where each list
* contains the available SAIs in an individual cell.
*/
- public void onAvailableSaisUpdated(List<Integer> currentSais,
- List<List<Integer>> availableSais) {
- }
+ void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+ @NonNull List<List<Integer>> availableSais);
/**
* Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
- * via this callback may be used to establish a data-link interface with the modem before the
- * middleware is ready.
- * Note that this method may be called before {@link #onMiddlewareReady()}.
+ * via this callback may be used to establish a data-link interface with the modem.
+ *
+ * In order to establish the data-link interface, the multicast IP and port must be obtained
+ * out-of-band from the carrier. A {@link java.net.MulticastSocket} may then be constructed
+ * using a {@link java.net.NetworkInterface} with the name and interface supplied by this
+ * callback.
*
* @param interfaceName The interface name for the data link.
* @param index The index for the data link.
*/
- public void onServiceInterfaceAvailable(String interfaceName, int index) {
- }
+ void onServiceInterfaceAvailable(@NonNull String interfaceName, int index);
/**
* Called to indicate that the middleware has been initialized and is ready.
@@ -94,6 +95,5 @@
* delivered via {@link #onError(int, String)} with error code
* {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
*/
- public void onMiddlewareReady() {
- }
+ void onMiddlewareReady();
}
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
index 721256a..44cc24a 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
@@ -29,11 +29,11 @@
void stopGroupCall(int subId, long tmgi);
- void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
- in int[] frequencyArray);
+ void updateGroupCall(int subscriptionId, long tmgi, in List saiList,
+ in List frequencyList);
- int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
- in int[] frequencyArray, IGroupCallCallback callback);
+ int startGroupCall(int subscriptionId, long tmgi, in List saiList,
+ in List frequencyList, IGroupCallCallback callback);
void dispose(int subId);
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
index 3734ca7..e86a47d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
@@ -115,15 +115,16 @@
}
@Override
- public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
- int[] frequencyArray) {
+ public void updateGroupCall(int subscriptionId, long tmgi, List saiList,
+ List frequencyList) {
MbmsGroupCallServiceBase.this.updateGroupCall(
- subscriptionId, tmgi, saiArray, frequencyArray);
+ subscriptionId, tmgi, saiList, frequencyList);
}
@Override
- public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray,
- final int[] frequencyArray, final IGroupCallCallback callback)
+ public int startGroupCall(final int subscriptionId, final long tmgi,
+ final List saiList,
+ final List frequencyList, final IGroupCallCallback callback)
throws RemoteException {
if (callback == null) {
throw new NullPointerException("Callback must not be null");
@@ -132,7 +133,7 @@
final int uid = Binder.getCallingUid();
int result = MbmsGroupCallServiceBase.this.startGroupCall(
- subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() {
+ subscriptionId, tmgi, saiList, frequencyList, new GroupCallCallback() {
@Override
public void onError(final int errorCode, final String message) {
try {
@@ -209,13 +210,13 @@
*
* @param subscriptionId The subscription id to use.
* @param tmgi The TMGI, an identifier for the group call.
- * @param saiArray An array of SAIs for the group call.
- * @param frequencyArray An array of frequencies for the group call.
+ * @param saiList A list of SAIs for the group call.
+ * @param frequencyList A list of frequencies for the group call.
* @param callback The callback object on which the app wishes to receive updates.
* @return Any error in {@link MbmsErrors.GeneralErrors}
*/
- public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray,
- GroupCallCallback callback) {
+ public int startGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+ List<Integer> frequencyList, GroupCallCallback callback) {
throw new UnsupportedOperationException("Not implemented");
}
@@ -237,11 +238,11 @@
/**
* Called when the app receives new SAI and frequency information for the group call identified
* by {@code tmgi}.
- * @param saiArray New array of SAIs that the call is available on.
- * @param frequencyArray New array of frequencies that the call is available on.
+ * @param saiList New list of SAIs that the call is available on.
+ * @param frequencyList New list of frequencies that the call is available on.
*/
- public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
- int[] frequencyArray) {
+ public void updateGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+ List<Integer> frequencyList) {
throw new UnsupportedOperationException("Not implemented");
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f0e8586..fc42de5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1372,6 +1372,15 @@
String getSubscriptionPreciseCarrierName(int subId);
/**
+ * Returns carrier id based on MCCMNC only. This will return a MNO carrier id used for fallback
+ * check when exact carrier id {@link #getSimCarrierId()} configurations are not found
+ *
+ * @return carrier id from passing mccmnc.
+ * @hide
+ */
+ int getCarrierIdFromMccMnc(int slotIndex, String mccmnc);
+
+ /**
* Action set from carrier signalling broadcast receivers to enable/disable metered apns
* Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
* @param subId the subscription ID that this action applies to.
@@ -1587,35 +1596,31 @@
/**
* Adds an IMS registration status callback for the subscription id specified.
*/
- void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
- String callingPackage);
+ void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c);
/**
* Removes an existing IMS registration status callback for the subscription specified.
*/
- void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
- String callingPackage);
+ void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c);
/**
* Adds an IMS MmTel capabilities callback for the subscription specified.
*/
- void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
- String callingPackage);
+ void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c);
/**
* Removes an existing IMS MmTel capabilities callback for the subscription specified.
*/
- void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
- String callingPackage);
+ void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c);
/**
* return true if the IMS MmTel capability for the given registration tech is capable.
*/
- boolean isCapable(int subId, int capability, int regTech, String callingPackage);
+ boolean isCapable(int subId, int capability, int regTech);
/**
* return true if the IMS MmTel capability for the given registration tech is available.
*/
- boolean isAvailable(int subId, int capability, int regTech, String callingPackage);
+ boolean isAvailable(int subId, int capability, int regTech);
/**
* Returns true if the user's setting for 4G LTE is enabled, for the subscription specified.
@@ -1630,7 +1635,7 @@
/**
* return true if the user's setting for VT is enabled for the subscription.
*/
- boolean isVtSettingEnabled(int subId, String callingPackage);
+ boolean isVtSettingEnabled(int subId);
/**
* Modify the user's setting for whether or not VT is available for the subscrption specified.
@@ -1704,7 +1709,7 @@
* Identify if the number is emergency number, based on all the active subscriptions.
*/
boolean isCurrentEmergencyNumber(String number);
-
+
/**
* Return a list of certs in hex string from loaded carrier privileges access rules.
*/
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index f91d74a..7842a1c 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -135,6 +135,7 @@
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
+ method public void updateServiceGroup(android.content.ServiceConnection, int, int);
}
public deprecated class MockCursor implements android.database.Cursor {
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 66be6d9..ae6cd29 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -591,6 +591,11 @@
}
@Override
+ public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void unbindService(ServiceConnection conn) {
throw new UnsupportedOperationException();
}
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/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index 771faaf..be1a455 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -47,6 +47,7 @@
assertNull(c.getEncryption());
assertNull(c.getAuthentication());
assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
+ assertEquals(0, c.getXfrmInterfaceId());
}
private IpSecConfig getSampleConfig() {
@@ -77,6 +78,7 @@
c.setNattKeepaliveInterval(42);
c.setMarkValue(12);
c.setMarkMask(23);
+ c.setXfrmInterfaceId(34);
return c;
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 9b919abf..4dc0341 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -71,6 +71,9 @@
private final LinkAddress mLocalInnerAddress;
private final int mFamily;
+ private static final int[] ADDRESS_FAMILIES =
+ new int[] {AF_INET, AF_INET6};
+
@Parameterized.Parameters
public static Collection ipSecConfigs() {
return Arrays.asList(
@@ -196,6 +199,7 @@
anyString(),
eq(TEST_SPI),
anyInt(),
+ anyInt(),
anyInt());
// Verify quota and RefcountedResource objects cleaned up
@@ -231,6 +235,7 @@
anyString(),
eq(TEST_SPI),
anyInt(),
+ anyInt(),
anyInt());
// Verify quota and RefcountedResource objects cleaned up
@@ -304,7 +309,8 @@
eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0),
eq(config.getEncapType()),
eq(encapSocketPort),
- eq(config.getEncapRemotePort()));
+ eq(config.getEncapRemotePort()),
+ eq(config.getXfrmInterfaceId()));
}
@Test
@@ -430,6 +436,7 @@
anyString(),
eq(TEST_SPI),
anyInt(),
+ anyInt(),
anyInt());
// quota is not released until the SPI is released by the Transform
assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
@@ -452,6 +459,7 @@
anyString(),
eq(TEST_SPI),
anyInt(),
+ anyInt(),
anyInt());
// Verify quota and RefcountedResource objects cleaned up
@@ -469,6 +477,7 @@
anyString(),
anyInt(),
anyInt(),
+ anyInt(),
anyInt());
assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
@@ -504,6 +513,7 @@
anyString(),
eq(TEST_SPI),
anyInt(),
+ anyInt(),
anyInt());
// Verify quota and RefcountedResource objects cleaned up
@@ -572,11 +582,12 @@
assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
verify(mMockNetd)
- .addVirtualTunnelInterface(
+ .ipSecAddTunnelInterface(
eq(createTunnelResp.interfaceName),
eq(mSourceAddr),
eq(mDestinationAddr),
anyInt(),
+ anyInt(),
anyInt());
}
@@ -591,7 +602,7 @@
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
- verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+ verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
try {
userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
createTunnelResp.resourceId);
@@ -614,7 +625,7 @@
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
- verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+ verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
try {
userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
createTunnelResp.resourceId);
@@ -624,6 +635,41 @@
}
@Test
+ public void testApplyTunnelModeTransform() throws Exception {
+ IpSecConfig ipSecConfig = new IpSecConfig();
+ ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+ addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+ addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ IpSecTunnelInterfaceResponse createTunnelResp =
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+
+ int transformResourceId = createTransformResp.resourceId;
+ int tunnelResourceId = createTunnelResp.resourceId;
+ mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
+ transformResourceId, "blessedPackage");
+
+ for (int selAddrFamily : ADDRESS_FAMILIES) {
+ verify(mMockNetd)
+ .ipSecUpdateSecurityPolicy(
+ eq(mUid),
+ eq(selAddrFamily),
+ eq(IpSecManager.DIRECTION_OUT),
+ anyString(),
+ anyString(),
+ eq(TEST_SPI),
+ anyInt(), // iKey/oKey
+ anyInt(), // mask
+ eq(tunnelResourceId));
+ }
+
+ ipSecConfig.setXfrmInterfaceId(tunnelResourceId);
+ verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+ }
+
+ @Test
public void testAddRemoveAddressFromTunnelInterface() throws Exception {
for (String pkgName : new String[]{"blessedPackage", "systemPackage"}) {
IpSecTunnelInterfaceResponse createTunnelResp =
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/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index af7123b..f2bd770 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -27,9 +27,17 @@
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.os.Process.SYSTEM_UID;
+import static com.android.server.connectivity.PermissionMonitor.NETWORK;
+import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
+
+import static junit.framework.Assert.fail;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
@@ -40,6 +48,8 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.INetworkManagementService;
+import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -48,12 +58,19 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.HashMap;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PermissionMonitorTest {
- private static final int MOCK_UID = 10001;
- private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+ private static final int MOCK_USER1 = 0;
+ private static final int MOCK_USER2 = 1;
+ private static final int MOCK_UID1 = 10001;
+ private static final String MOCK_PACKAGE1 = "appName1";
+ private static final String SYSTEM_PACKAGE1 = "sysName1";
+ private static final String SYSTEM_PACKAGE2 = "sysName2";
private static final String PARTITION_SYSTEM = "system";
private static final String PARTITION_OEM = "oem";
private static final String PARTITION_PRODUCT = "product";
@@ -63,6 +80,7 @@
@Mock private Context mContext;
@Mock private PackageManager mPackageManager;
+ @Mock private INetworkManagementService mNMS;
private PermissionMonitor mPermissionMonitor;
@@ -70,8 +88,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
- when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES);
- mPermissionMonitor = spy(new PermissionMonitor(mContext, null));
+ mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS));
}
private boolean hasBgPermission(String partition, int targetSdkVersion, int uid,
@@ -80,7 +97,8 @@
packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
packageInfo.applicationInfo.uid = uid;
when(mPackageManager.getPackageInfoAsUser(
- eq(MOCK_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+ eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[] {MOCK_PACKAGE1});
return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
}
@@ -143,16 +161,16 @@
@Test
public void testHasUseBackgroundNetworksPermission() throws Exception {
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID,
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1,
CONNECTIVITY_USE_RESTRICTED_NETWORKS));
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID));
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
}
@Test
@@ -172,15 +190,150 @@
@Test
public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception {
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID,
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1,
CONNECTIVITY_USE_RESTRICTED_NETWORKS));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
- assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID));
- assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+ assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+ assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+ }
+
+ private class NMSMonitor {
+ private final HashMap<Integer, Boolean> mApps = new HashMap<>();
+
+ NMSMonitor(INetworkManagementService mockNMS) throws Exception {
+ // Add hook to verify and track result of setPermission.
+ doAnswer((InvocationOnMock invocation) -> {
+ final Object[] args = invocation.getArguments();
+ final Boolean isSystem = args[0].equals("SYSTEM");
+ for (final int uid : (int[]) args[1]) {
+ // TODO: Currently, permission monitor will send duplicate commands for each uid
+ // corresponding to each user. Need to fix that and uncomment below test.
+ // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) {
+ // fail("uid " + uid + " is already set to " + isSystem);
+ // }
+ mApps.put(uid, isSystem);
+ }
+ return null;
+ }).when(mockNMS).setPermission(anyString(), any(int[].class));
+
+ // Add hook to verify and track result of clearPermission.
+ doAnswer((InvocationOnMock invocation) -> {
+ final Object[] args = invocation.getArguments();
+ for (final int uid : (int[]) args[0]) {
+ // TODO: Currently, permission monitor will send duplicate commands for each uid
+ // corresponding to each user. Need to fix that and uncomment below test.
+ // if (!mApps.containsKey(uid)) {
+ // fail("uid " + uid + " does not exist.");
+ // }
+ mApps.remove(uid);
+ }
+ return null;
+ }).when(mockNMS).clearPermission(any(int[].class));
+ }
+
+ public void expectPermission(Boolean permission, int[] users, int[] apps) {
+ for (final int user : users) {
+ for (final int app : apps) {
+ final int uid = UserHandle.getUid(user, app);
+ if (!mApps.containsKey(uid)) {
+ fail("uid " + uid + " does not exist.");
+ }
+ if (mApps.get(uid) != permission) {
+ fail("uid " + uid + " has wrong permission: " + permission);
+ }
+ }
+ }
+ }
+
+ public void expectNoPermission(int[] users, int[] apps) {
+ for (final int user : users) {
+ for (final int app : apps) {
+ final int uid = UserHandle.getUid(user, app);
+ if (mApps.containsKey(uid)) {
+ fail("uid " + uid + " has listed permissions, expected none.");
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testUserAndPackageAddRemove() throws Exception {
+ final NMSMonitor mNMSMonitor = new NMSMonitor(mNMS);
+
+ // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
+ // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
+ // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
+ doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
+ doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
+ eq(SYSTEM_PACKAGE1));
+ doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+ eq(SYSTEM_PACKAGE2));
+ doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+ eq(MOCK_PACKAGE1));
+
+ // Add SYSTEM_PACKAGE2, expect only have network permission.
+ mPermissionMonitor.onUserAdded(MOCK_USER1);
+ addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+ // Add SYSTEM_PACKAGE1, expect permission escalate.
+ addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
+ mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+ mPermissionMonitor.onUserAdded(MOCK_USER2);
+ mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+
+ addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
+ mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{MOCK_UID1});
+
+ // Remove MOCK_UID1, expect no permission left for all user.
+ mPermissionMonitor.onPackageRemoved(MOCK_UID1);
+ removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
+ mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
+
+ // Remove SYSTEM_PACKAGE1, expect permission downgrade.
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
+ removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID);
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+
+ mPermissionMonitor.onUserRemoved(MOCK_USER1);
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID});
+
+ // Remove all packages, expect no permission left.
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
+ removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID);
+ mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID, MOCK_UID1});
+
+ // Remove last user, expect no redundant clearPermission is invoked.
+ mPermissionMonitor.onUserRemoved(MOCK_USER2);
+ mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID, MOCK_UID1});
+ }
+
+ // Normal package add/remove operations will trigger multiple intent for uids corresponding to
+ // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be
+ // called multiple times with the uid corresponding to each user.
+ private void addPackageForUsers(int[] users, String packageName, int uid) {
+ for (final int user : users) {
+ mPermissionMonitor.onPackageAdded(packageName, UserHandle.getUid(user, uid));
+ }
+ }
+
+ private void removePackageForUsers(int[] users, int uid) {
+ for (final int user : users) {
+ mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid));
+ }
}
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a6ed9f2..8081812 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -23,15 +23,15 @@
import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
@@ -39,19 +39,18 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
+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 static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.mock;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -91,9 +90,9 @@
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
-import android.os.test.TestLooper;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.test.TestLooper;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -126,7 +125,6 @@
public class TetheringTest {
private static final int IFINDEX_OFFSET = 100;
- private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
private static final String TEST_USB_IFNAME = "test_rndis0";
@@ -370,61 +368,6 @@
mServiceContext.unregisterReceiver(mBroadcastReceiver);
}
- private void setupForRequiredProvisioning() {
- // Produce some acceptable looking provision app setting if requested.
- when(mResources.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
- .thenReturn(PROVISIONING_APP_NAME);
- // Don't disable tethering provisioning unless requested.
- when(mSystemProperties.getBoolean(eq(Tethering.DISABLE_PROVISIONING_SYSPROP_KEY),
- anyBoolean())).thenReturn(false);
- // Act like the CarrierConfigManager is present and ready unless told otherwise.
- when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
- .thenReturn(mCarrierConfigManager);
- when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
- mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
- }
-
- @Test
- public void canRequireProvisioning() {
- setupForRequiredProvisioning();
- sendConfigurationChanged();
- assertTrue(mTethering.isTetherProvisioningRequired());
- }
-
- @Test
- public void toleratesCarrierConfigManagerMissing() {
- setupForRequiredProvisioning();
- when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
- .thenReturn(null);
- sendConfigurationChanged();
- // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
- // We therefore still require provisioning.
- assertTrue(mTethering.isTetherProvisioningRequired());
- }
-
- @Test
- public void toleratesCarrierConfigMissing() {
- setupForRequiredProvisioning();
- when(mCarrierConfigManager.getConfig()).thenReturn(null);
- sendConfigurationChanged();
- // We still have a provisioning app configured, so still require provisioning.
- assertTrue(mTethering.isTetherProvisioningRequired());
- }
-
- @Test
- public void provisioningNotRequiredWhenAppNotFound() {
- setupForRequiredProvisioning();
- when(mResources.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
- .thenReturn(null);
- assertTrue(!mTethering.isTetherProvisioningRequired());
- when(mResources.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
- .thenReturn(new String[] {"malformedApp"});
- assertTrue(!mTethering.isTetherProvisioningRequired());
- }
-
private void sendWifiApStateChanged(int state) {
final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
intent.putExtra(EXTRA_WIFI_AP_STATE, state);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
new file mode 100644
index 0000000..0f72229
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.connectivity.tethering;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.os.PersistableBundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CarrierConfigManager;
+
+import com.android.internal.R;
+import com.android.server.connectivity.MockableSystemProperties;
+
+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;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class EntitlementManagerTest {
+
+ private static final int EVENT_EM_UPDATE = 1;
+ private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+
+ @Mock private CarrierConfigManager mCarrierConfigManager;
+ @Mock private Context mContext;
+ @Mock private ContentResolver mContent;
+ @Mock private MockableSystemProperties mSystemProperties;
+ @Mock private Resources mResources;
+ @Mock private SharedLog mLog;
+
+ // Like so many Android system APIs, these cannot be mocked because it is marked final.
+ // We have to use the real versions.
+ private final PersistableBundle mCarrierConfig = new PersistableBundle();
+
+ private EntitlementManager mEnMgr;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getContentResolver()).thenReturn(mContent);
+ when(mResources.getStringArray(R.array.config_tether_dhcp_range))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_usb_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getIntArray(R.array.config_tether_upstream_types))
+ .thenReturn(new int[0]);
+ when(mLog.forSubComponent(anyString())).thenReturn(mLog);
+
+ mEnMgr = new EntitlementManager(mContext, mLog, mSystemProperties);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ }
+
+ @After
+ public void tearDown() throws Exception {}
+
+ private void setupForRequiredProvisioning() {
+ // Produce some acceptable looking provision app setting if requested.
+ when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(PROVISIONING_APP_NAME);
+ // Don't disable tethering provisioning unless requested.
+ when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY),
+ anyBoolean())).thenReturn(false);
+ // Act like the CarrierConfigManager is present and ready unless told otherwise.
+ when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+ .thenReturn(mCarrierConfigManager);
+ when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
+ mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+ }
+
+ @Test
+ public void canRequireProvisioning() {
+ setupForRequiredProvisioning();
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ assertTrue(mEnMgr.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void toleratesCarrierConfigManagerMissing() {
+ setupForRequiredProvisioning();
+ when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+ .thenReturn(null);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
+ // Therefore provisioning still be required.
+ assertTrue(mEnMgr.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void toleratesCarrierConfigMissing() {
+ setupForRequiredProvisioning();
+ when(mCarrierConfigManager.getConfig()).thenReturn(null);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ // We still have a provisioning app configured, so still require provisioning.
+ assertTrue(mEnMgr.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void provisioningNotRequiredWhenAppNotFound() {
+ setupForRequiredProvisioning();
+ when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(null);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ assertFalse(mEnMgr.isTetherProvisioningRequired());
+ when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(new String[] {"malformedApp"});
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ assertFalse(mEnMgr.isTetherProvisioningRequired());
+ }
+
+}
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/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/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/media/java/android/media/ISessionTokensListener.aidl b/tools/powermodel/src/com/android/powermodel/Component.java
similarity index 66%
copy from media/java/android/media/ISessionTokensListener.aidl
copy to tools/powermodel/src/com/android/powermodel/Component.java
index c83a19e..baae6d7 100644
--- a/media/java/android/media/ISessionTokensListener.aidl
+++ b/tools/powermodel/src/com/android/powermodel/Component.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,14 +14,21 @@
* limitations under the License.
*/
-package android.media;
-
-import android.os.Bundle;
+package com.android.powermodel;
/**
- * Listens for changes to the list of session tokens.
- * @hide
+ * The hardware components that use power on a device.
*/
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
+public enum Component {
+ CPU,
+ SCREEN,
+ MODEM,
+ WIFI,
+ BLUETOOTH,
+ VIDEO,
+ AUDIO,
+ FLASHLIGHT,
+ CAMERA,
+ GPS,
}
+
diff --git a/media/java/android/media/update/ProviderCreator.java b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
similarity index 74%
copy from media/java/android/media/update/ProviderCreator.java
copy to tools/powermodel/src/com/android/powermodel/ComponentProfile.java
index f5f3e47..e76e5fb 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,7 @@
* limitations under the License.
*/
-package android.media.update;
+package com.android.powermodel;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+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/RawBatteryStats.java b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java
new file mode 100644
index 0000000..d0c1790
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java
@@ -0,0 +1,953 @@
+/*
+ * 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="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="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/media/java/android/media/update/ProviderCreator.java b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
similarity index 65%
copy from media/java/android/media/update/ProviderCreator.java
copy to tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
index f5f3e47..63ff3a6 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,14 @@
* limitations under the License.
*/
-package android.media.update;
+package com.android.powermodel.component;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+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/media/java/android/media/update/ProviderCreator.java b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
similarity index 61%
copy from media/java/android/media/update/ProviderCreator.java
copy to tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
index f5f3e47..8f5e7d0 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,16 @@
* limitations under the License.
*/
-package android.media.update;
+package com.android.powermodel.component;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+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/media/java/android/media/update/ProviderCreator.java b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
similarity index 65%
copy from media/java/android/media/update/ProviderCreator.java
copy to tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
index f5f3e47..8ee22d0 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,14 @@
* limitations under the License.
*/
-package android.media.update;
+package com.android.powermodel.component;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+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/media/java/android/media/update/ProviderCreator.java b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
similarity index 65%
copy from media/java/android/media/update/ProviderCreator.java
copy to tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
index f5f3e47..c85f3ff 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,14 @@
* limitations under the License.
*/
-package android.media.update;
+package com.android.powermodel.component;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+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/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/media/java/android/media/update/ProviderCreator.java b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
similarity index 61%
copy from media/java/android/media/update/ProviderCreator.java
copy to tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
index f5f3e47..e1051c6 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,16 @@
* limitations under the License.
*/
-package android.media.update;
+package com.android.powermodel.component;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+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/media/java/android/media/update/ProviderCreator.java b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
similarity index 65%
copy from media/java/android/media/update/ProviderCreator.java
copy to tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
index f5f3e47..5152795 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,15 @@
* limitations under the License.
*/
-package android.media.update;
+package com.android.powermodel.component;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+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/media/java/android/media/update/ProviderCreator.java b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
similarity index 62%
copy from media/java/android/media/update/ProviderCreator.java
copy to tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
index f5f3e47..6f424bf 100644
--- a/media/java/android/media/update/ProviderCreator.java
+++ b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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,10 +14,16 @@
* limitations under the License.
*/
-package android.media.update;
+package com.android.powermodel.component;
-/** @hide */
-@FunctionalInterface
-public interface ProviderCreator<T, U> {
- U createProvider(T instance);
+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..9a79a2d
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/util/Conversion.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+ }
+
+ /**
+ * No public constructor.
+ */
+ private Conversion() {
+ }
+}
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/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/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/stringslint/stringslint.py b/tools/stringslint/stringslint.py
index 03c0b9a..afe91cd 100644
--- a/tools/stringslint/stringslint.py
+++ b/tools/stringslint/stringslint.py
@@ -145,6 +145,13 @@
if "translatable" in child.attrib and child.attrib["translatable"].lower() == "false":
continue
+ misspelled_attributes = [
+ ("translateable", "translatable"),
+ ]
+ for misspelling, expected in misspelled_attributes:
+ if misspelling in child.attrib:
+ error(child, "Misspelled <string> attribute.", misspelling, expected)
+
limit = re.search("CHAR[ _-]LIMIT=(\d+|NONE|none)", comment.text)
if limit is None:
info(child, "Missing CHAR LIMIT to aid translation",
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 3ec8a41..364d5084 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);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7aff03c..8dd6c77 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1067,7 +1067,7 @@
public List<WifiConfiguration> getConfiguredNetworks() {
try {
ParceledListSlice<WifiConfiguration> parceledList =
- mService.getConfiguredNetworks();
+ mService.getConfiguredNetworks(mContext.getOpPackageName());
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1761,8 +1761,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 +1785,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 +1809,7 @@
@Deprecated
public boolean reassociate() {
try {
- mService.reassociate(mContext.getOpPackageName());
- return true;
+ return mService.reassociate(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2132,14 +2129,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 +3403,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 +3428,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 +3459,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 +3483,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 +3503,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 +3520,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 {
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
index aa8d325..87706b9 100644
--- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -60,14 +60,27 @@
*/
private @Nullable Pair<MacAddress, MacAddress> mBssidPatternMatcher;
/**
+ * Whether this is an OWE network or not.
+ */
+ private boolean mIsEnhancedOpen;
+ /**
* Pre-shared key for use with WPA-PSK networks.
*/
- private @Nullable String mPskPassphrase;
+ private @Nullable String mWpa2PskPassphrase;
+ /**
+ * Pre-shared key for use with WPA3-SAE networks.
+ */
+ private @Nullable String mWpa3SaePassphrase;
/**
* The enterprise configuration details specifying the EAP method,
- * certificates and other settings associated with the EAP.
+ * certificates and other settings associated with the WPA-EAP networks.
*/
- private @Nullable WifiEnterpriseConfig mEnterpriseConfig;
+ private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig;
+ /**
+ * The enterprise configuration details specifying the EAP method,
+ * certificates and other settings associated with the SuiteB networks.
+ */
+ private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
/**
* This is a network that does not broadcast its SSID, so an
* SSID-specific probe request must be used for scans.
@@ -94,8 +107,11 @@
public WifiNetworkConfigBuilder() {
mSsidPatternMatcher = null;
mBssidPatternMatcher = null;
- mPskPassphrase = null;
- mEnterpriseConfig = null;
+ mIsEnhancedOpen = false;
+ mWpa2PskPassphrase = null;
+ mWpa3SaePassphrase = null;
+ mWpa2EnterpriseConfig = null;
+ mWpa3EnterpriseConfig = null;
mIsHiddenSSID = false;
mIsAppInteractionRequired = false;
mIsUserInteractionRequired = false;
@@ -188,36 +204,81 @@
}
/**
- * Set the ASCII PSK passphrase for this network. Needed for authenticating to
- * WPA_PSK networks.
+ * Specifies whether this represents an Enhanced Open (OWE) network.
*
- * @param pskPassphrase PSK passphrase of the network.
+ * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+ * method.
+ */
+ public WifiNetworkConfigBuilder setIsEnhancedOpen() {
+ mIsEnhancedOpen = true;
+ return this;
+ }
+
+ /**
+ * Set the ASCII WPA2 passphrase for this network. Needed for authenticating to
+ * WPA2-PSK networks.
+ *
+ * @param passphrase passphrase of the network.
* @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
* method.
* @throws IllegalArgumentException if the passphrase is not ASCII encodable.
*/
- public WifiNetworkConfigBuilder setPskPassphrase(@NonNull String pskPassphrase) {
- checkNotNull(pskPassphrase);
+ public WifiNetworkConfigBuilder setWpa2Passphrase(@NonNull String passphrase) {
+ checkNotNull(passphrase);
final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
- if (!asciiEncoder.canEncode(pskPassphrase)) {
+ if (!asciiEncoder.canEncode(passphrase)) {
throw new IllegalArgumentException("passphrase not ASCII encodable");
}
- mPskPassphrase = pskPassphrase;
+ mWpa2PskPassphrase = passphrase;
+ return this;
+ }
+
+ /**
+ * Set the ASCII WPA3 passphrase for this network. Needed for authenticating to
+ * WPA3-SAE networks.
+ *
+ * @param passphrase passphrase of the network.
+ * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+ * method.
+ * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
+ */
+ public WifiNetworkConfigBuilder setWpa3Passphrase(@NonNull String passphrase) {
+ checkNotNull(passphrase);
+ final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
+ if (!asciiEncoder.canEncode(passphrase)) {
+ throw new IllegalArgumentException("passphrase not ASCII encodable");
+ }
+ mWpa3SaePassphrase = passphrase;
return this;
}
/**
* Set the associated enterprise configuration for this network. Needed for authenticating to
- * WPA_EAP networks. See {@link WifiEnterpriseConfig} for description.
+ * WPA2-EAP networks. See {@link WifiEnterpriseConfig} for description.
*
* @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
* @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
* method.
*/
- public WifiNetworkConfigBuilder setEnterpriseConfig(
+ public WifiNetworkConfigBuilder setWpa2EnterpriseConfig(
@NonNull WifiEnterpriseConfig enterpriseConfig) {
checkNotNull(enterpriseConfig);
- mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+ mWpa2EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+ return this;
+ }
+
+ /**
+ * Set the associated enterprise configuration for this network. Needed for authenticating to
+ * WPA3-SuiteB networks. See {@link WifiEnterpriseConfig} for description.
+ *
+ * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+ * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+ * method.
+ */
+ public WifiNetworkConfigBuilder setWpa3EnterpriseConfig(
+ @NonNull WifiEnterpriseConfig enterpriseConfig) {
+ checkNotNull(enterpriseConfig);
+ mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
return this;
}
@@ -324,16 +385,38 @@
configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
}
- private void setKeyMgmtInWifiConfiguration(@NonNull WifiConfiguration configuration) {
- if (!TextUtils.isEmpty(mPskPassphrase)) {
- // WPA_PSK network.
+ private void setSecurityParamsInWifiConfiguration(@NonNull WifiConfiguration configuration) {
+ if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
- } else if (mEnterpriseConfig != null) {
- // WPA_EAP network
+ // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+ configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\"";
+ } else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network.
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
+ // PMF mandatory for SAE.
+ configuration.requirePMF = true;
+ // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+ configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\"";
+ } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
- } else {
- // Open network
+ configuration.enterpriseConfig = mWpa2EnterpriseConfig;
+ } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
+ // TODO (b/113878056): Verify these params once we verify SuiteB configuration.
+ configuration.allowedGroupMgmtCiphers.set(
+ WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
+ configuration.allowedSuiteBCiphers.set(
+ WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
+ configuration.allowedSuiteBCiphers.set(
+ WifiConfiguration.SuiteBCipher.ECDHE_RSA);
+ configuration.requirePMF = true;
+ configuration.enterpriseConfig = mWpa3EnterpriseConfig;
+ } else if (mIsEnhancedOpen) { // OWE network
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
+ // PMF mandatory.
+ configuration.requirePMF = true;
+ } else { // Open network
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
}
}
@@ -349,12 +432,7 @@
if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
}
- setKeyMgmtInWifiConfiguration(wifiConfiguration);
- // WifiConfiguration.preSharedKey needs quotes around ASCII password.
- if (mPskPassphrase != null) {
- wifiConfiguration.preSharedKey = "\"" + mPskPassphrase + "\"";
- }
- wifiConfiguration.enterpriseConfig = mEnterpriseConfig;
+ setSecurityParamsInWifiConfiguration(wifiConfiguration);
wifiConfiguration.hiddenSSID = mIsHiddenSSID;
wifiConfiguration.priority = mPriority;
wifiConfiguration.meteredOverride =
@@ -396,6 +474,20 @@
return false;
}
+ private void validateSecurityParams() {
+ int numSecurityTypes = 0;
+ numSecurityTypes += mIsEnhancedOpen ? 1 : 0;
+ numSecurityTypes += !TextUtils.isEmpty(mWpa2PskPassphrase) ? 1 : 0;
+ numSecurityTypes += !TextUtils.isEmpty(mWpa3SaePassphrase) ? 1 : 0;
+ numSecurityTypes += mWpa2EnterpriseConfig != null ? 1 : 0;
+ numSecurityTypes += mWpa3EnterpriseConfig != null ? 1 : 0;
+ if (numSecurityTypes > 1) {
+ throw new IllegalStateException("only one of setIsEnhancedOpen, setWpa2Passphrase,"
+ + "setWpa3Passphrase, setWpa2EnterpriseConfig or setWpa3EnterpriseConfig"
+ + " can be invoked for network specifier");
+ }
+ }
+
/**
* Create a specifier object used to request a Wi-Fi network. The generated
* {@link NetworkSpecifier} should be used in
@@ -464,10 +556,7 @@
+ "setIsUserInteractionRequired/setPriority/setIsMetered are allowed for "
+ "specifier");
}
- if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
- throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
- + " be invoked for network specifier");
- }
+ validateSecurityParams();
return new WifiNetworkSpecifier(
mSsidPatternMatcher,
@@ -493,10 +582,7 @@
throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are"
+ " allowed for suggestion");
}
- if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
- throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
- + "be invoked for suggestion");
- }
+ validateSecurityParams();
return new WifiNetworkSuggestion(
buildWifiConfiguration(),
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 529548f..6622a25 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}
*/
@@ -798,6 +801,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 +816,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 +828,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 +866,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 +881,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 +892,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/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
index 25dcdd8..893b19c 100644
--- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
+++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
@@ -49,7 +49,7 @@
/**
* SSID of the network to connect for service sign-up.
*/
- private final WifiSsid mOsuSsid;
+ private WifiSsid mOsuSsid;
/**
* Friendly name of the OSU provider.
@@ -130,6 +130,10 @@
return mOsuSsid;
}
+ public void setOsuSsid(WifiSsid osuSsid) {
+ mOsuSsid = osuSsid;
+ }
+
public String getFriendlyName() {
return mFriendlyName;
}
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index 5c9db53..a62d63c 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -150,6 +150,12 @@
public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22;
/**
+ * The reason code for provisioning failure when an {@link OsuProvider} is not found for
+ * provisioning.
+ */
+ public static final int OSU_FAILURE_OSU_PROVIDER_NOT_FOUND = 23;
+
+ /**
* The status code for provisioning flow to indicate connecting to OSU AP
*/
public static final int OSU_STATUS_AP_CONNECTING = 1;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index f58a006..d0efbcf 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;
@@ -1616,4 +1706,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..aa526d2 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();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index 8980ddb..c455c6f 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.net.MacAddress;
@@ -81,11 +82,11 @@
* pattern.
*/
@Test
- public void testWifiNetworkSpecifierBuilderForWpaPskNetworkWithBssidPattern() {
+ public void testWifiNetworkSpecifierBuilderForWpa2PskNetworkWithBssidPattern() {
NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
.setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK))
- .setPskPassphrase(TEST_PRESHARED_KEY)
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
.buildNetworkSpecifier();
assertTrue(specifier instanceof WifiNetworkSpecifier);
@@ -119,7 +120,7 @@
* SSID and BSSID pattern.
*/
@Test
- public void testWifiNetworkSpecifierBuilderForEnterpriseHiddenNetworkWithSsidAndBssid() {
+ public void testWifiNetworkSpecifierBuilderForWpa2EapHiddenNetworkWithSsidAndBssid() {
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
@@ -127,7 +128,7 @@
NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
.setBssid(MacAddress.fromString(TEST_BSSID))
- .setEnterpriseConfig(enterpriseConfig)
+ .setWpa2EnterpriseConfig(enterpriseConfig)
.setIsHiddenSsid()
.buildNetworkSpecifier();
@@ -174,14 +175,14 @@
}
/**
- * Ensure {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} throws an exception
+ * Ensure {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} throws an exception
* when the string is not ASCII encodable.
*/
@Test(expected = IllegalArgumentException.class)
- public void testSetPskPassphraseWithNonAsciiString() {
+ public void testSetWpa2PasphraseWithNonAsciiString() {
new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
- .setPskPassphrase("salvē")
+ .setWpa2Passphrase("salvē")
.buildNetworkSpecifier();
}
@@ -275,15 +276,15 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
- * when both {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} and
- * {@link WifiNetworkConfigBuilder#setEnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+ * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setWpa2EnterpriseConfig(WifiEnterpriseConfig)} are invoked.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSpecifierBuilderWithBothPskPassphraseAndEnterpriseConfig() {
+ public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndEnterpriseConfig() {
new WifiNetworkConfigBuilder()
.setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
- .setPskPassphrase(TEST_PRESHARED_KEY)
- .setEnterpriseConfig(new WifiEnterpriseConfig())
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
+ .setWpa2EnterpriseConfig(new WifiEnterpriseConfig())
.buildNetworkSpecifier();
}
@@ -375,10 +376,11 @@
* app interaction and has a priority of zero set.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForWpaEapNetworkWithPriorityAndReqAppInteraction() {
+ public void
+ testWifiNetworkSuggestionBuilderForWpa2EapNetworkWithPriorityAndReqAppInteraction() {
WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
- .setPskPassphrase(TEST_PRESHARED_KEY)
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
.setIsAppInteractionRequired()
.setPriority(0)
.buildNetworkSuggestion();
@@ -401,10 +403,11 @@
* user interaction and is metered.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForWpaPskNetworkWithMeteredAndReqUserInteraction() {
+ public void
+ testWifiNetworkSuggestionBuilderForWpa2PskNetworkWithMeteredAndReqUserInteraction() {
WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
- .setPskPassphrase(TEST_PRESHARED_KEY)
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
.setIsUserInteractionRequired()
.setIsMetered()
.buildNetworkSuggestion();
@@ -422,6 +425,74 @@
}
/**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for OWE network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetwork() {
+ WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setIsEnhancedOpen()
+ .buildNetworkSuggestion();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.OWE));
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ assertTrue(suggestion.wifiConfiguration.requirePMF);
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SAE network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3PskNetwork() {
+ WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .buildNetworkSuggestion();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SAE));
+ assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+ suggestion.wifiConfiguration.preSharedKey);
+ assertTrue(suggestion.wifiConfiguration.requirePMF);
+ }
+
+
+ /**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SuiteB network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3EapNetwork() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
+
+ WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setWpa3EnterpriseConfig(enterpriseConfig)
+ .buildNetworkSuggestion();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.GCMP_256));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupMgmtCiphers
+ .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+ assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
+ .get(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA));
+ assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
+ .get(WifiConfiguration.SuiteBCipher.ECDHE_RSA));
+ assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ }
+
+ /**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
* when {@link WifiNetworkConfigBuilder#setSsidPattern(PatternMatcher)} is set.
*/
@@ -478,4 +549,46 @@
.setPriority(-1)
.buildNetworkSuggestion();
}
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndWpa3Passphrase() {
+ new WifiNetworkConfigBuilder()
+ .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnterprise() {
+ new WifiNetworkConfigBuilder()
+ .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .setWpa3EnterpriseConfig(new WifiEnterpriseConfig())
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setIsEnhancedOpen(} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnhancedOpen() {
+ new WifiNetworkConfigBuilder()
+ .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .setIsEnhancedOpen()
+ .buildNetworkSpecifier();
+ }
}