Merge "Undeprecate injector API to allow change summary"
diff --git a/Android.bp b/Android.bp
index 4762cba..32bd408 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,6 +104,7 @@
"core/java/android/app/backup/IRestoreObserver.aidl",
"core/java/android/app/backup/IRestoreSession.aidl",
"core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
+ "core/java/android/app/role/IOnRoleHoldersChangedListener.aidl",
"core/java/android/app/role/IRoleManager.aidl",
"core/java/android/app/role/IRoleManagerCallback.aidl",
"core/java/android/app/slice/ISliceManager.aidl",
@@ -690,7 +691,6 @@
"frameworks/av/media/libaudioclient/aidl",
"frameworks/native/aidl/gui",
"system/core/storaged/binder",
- "system/netd/server/binder",
"system/vold/binder",
"system/bt/binder",
"system/security/keystore/binder",
diff --git a/api/current.txt b/api/current.txt
index d549df39..e600870 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -78,6 +78,7 @@
field public static final java.lang.String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE";
field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
+ field public static final java.lang.String GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY = "android.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY";
field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
@@ -90,6 +91,7 @@
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
+ field public static final java.lang.String CALL_COMPANION_APP = "android.permission.CALL_COMPANION_APP";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -5948,6 +5950,15 @@
field public static final int STYLE_SPINNER = 0; // 0x0
}
+ public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
+ ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction);
+ method public int describeContents();
+ method public android.app.RemoteAction getUserAction();
+ method public java.lang.CharSequence getUserMessage();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR;
+ }
+
public final class RemoteAction implements android.os.Parcelable {
ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.RemoteAction clone();
@@ -5973,6 +5984,7 @@
method public java.util.Set<java.lang.String> getAllowedDataTypes();
method public java.lang.CharSequence[] getChoices();
method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
+ method public int getEditChoicesBeforeSending();
method public android.os.Bundle getExtras();
method public java.lang.CharSequence getLabel();
method public java.lang.String getResultKey();
@@ -5982,6 +5994,9 @@
method public static void setResultsSource(android.content.Intent, int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
+ field public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0; // 0x0
+ field public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1; // 0x1
+ field public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2; // 0x2
field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
field public static final int SOURCE_CHOICE = 1; // 0x1
@@ -5996,6 +6011,7 @@
method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
+ method public android.app.RemoteInput.Builder setEditChoicesBeforeSending(int);
method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
}
@@ -6433,6 +6449,13 @@
field public static final android.os.Parcelable.Creator<android.app.admin.ConnectEvent> CREATOR;
}
+ public class DelegatedAdminReceiver extends android.content.BroadcastReceiver {
+ ctor public DelegatedAdminReceiver();
+ method public java.lang.String onChoosePrivateKeyAlias(android.content.Context, android.content.Intent, int, android.net.Uri, java.lang.String);
+ method public void onNetworkLogsAvailable(android.content.Context, android.content.Intent, long, int);
+ method public void onReceive(android.content.Context, android.content.Intent);
+ }
+
public final class DeviceAdminInfo implements android.os.Parcelable {
ctor public DeviceAdminInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
@@ -6495,11 +6518,13 @@
method public void onUserStarted(android.content.Context, android.content.Intent, android.os.UserHandle);
method public void onUserStopped(android.content.Context, android.content.Intent, android.os.UserHandle);
method public void onUserSwitched(android.content.Context, android.content.Intent, android.os.UserHandle);
+ field public static final java.lang.String ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
field public static final java.lang.String ACTION_LOCK_TASK_ENTERING = "android.app.action.LOCK_TASK_ENTERING";
field public static final java.lang.String ACTION_LOCK_TASK_EXITING = "android.app.action.LOCK_TASK_EXITING";
+ field public static final java.lang.String ACTION_NETWORK_LOGS_AVAILABLE = "android.app.action.NETWORK_LOGS_AVAILABLE";
field public static final java.lang.String ACTION_PASSWORD_CHANGED = "android.app.action.ACTION_PASSWORD_CHANGED";
field public static final java.lang.String ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING";
field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED";
@@ -6573,6 +6598,7 @@
method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(android.content.ComponentName);
method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
+ method public int getPasswordComplexity();
method public long getPasswordExpiration(android.content.ComponentName);
method public long getPasswordExpirationTimeout(android.content.ComponentName);
method public int getPasswordHistoryLength(android.content.ComponentName);
@@ -6744,10 +6770,13 @@
field public static final java.lang.String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
field public static final java.lang.String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall";
field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install";
+ field public static final java.lang.String DELEGATION_CERT_SELECTION = "delegation-cert-selection";
field public static final java.lang.String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app";
field public static final java.lang.String DELEGATION_INSTALL_EXISTING_PACKAGE = "delegation-install-existing-package";
field public static final java.lang.String DELEGATION_KEEP_UNINSTALLED_PACKAGES = "delegation-keep-uninstalled-packages";
+ field public static final java.lang.String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";
field public static final java.lang.String DELEGATION_PACKAGE_ACCESS = "delegation-package-access";
+ field public static final java.lang.String DELEGATION_PACKAGE_INSTALLATION = "delegation-package-installation";
field public static final java.lang.String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant";
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
@@ -6826,6 +6855,10 @@
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
+ field public static final int PASSWORD_COMPLEXITY_HIGH = 327680; // 0x50000
+ field public static final int PASSWORD_COMPLEXITY_LOW = 65536; // 0x10000
+ field public static final int PASSWORD_COMPLEXITY_MEDIUM = 196608; // 0x30000
+ field public static final int PASSWORD_COMPLEXITY_NONE = 0; // 0x0
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
field public static final int PASSWORD_QUALITY_BIOMETRIC_WEAK = 32768; // 0x8000
@@ -7614,13 +7647,16 @@
method public java.lang.String getPackageName();
method public java.lang.String getShortcutId();
method public long getTimeStamp();
+ field public static final int ACTIVITY_PAUSED = 2; // 0x2
+ field public static final int ACTIVITY_RESUMED = 1; // 0x1
+ field public static final int ACTIVITY_STOPPED = 23; // 0x17
field public static final int CONFIGURATION_CHANGE = 5; // 0x5
field public static final int FOREGROUND_SERVICE_START = 19; // 0x13
field public static final int FOREGROUND_SERVICE_STOP = 20; // 0x14
field public static final int KEYGUARD_HIDDEN = 18; // 0x12
field public static final int KEYGUARD_SHOWN = 17; // 0x11
- field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
- field public static final int MOVE_TO_FOREGROUND = 1; // 0x1
+ field public static final deprecated int MOVE_TO_BACKGROUND = 2; // 0x2
+ field public static final deprecated int MOVE_TO_FOREGROUND = 1; // 0x1
field public static final int NONE = 0; // 0x0
field public static final int SCREEN_INTERACTIVE = 15; // 0xf
field public static final int SCREEN_NON_INTERACTIVE = 16; // 0x10
@@ -7637,9 +7673,11 @@
method public long getLastTimeForegroundServiceUsed();
method public long getLastTimeStamp();
method public long getLastTimeUsed();
+ method public long getLastTimeVisible();
method public java.lang.String getPackageName();
method public long getTotalTimeForegroundServiceUsed();
method public long getTotalTimeInForeground();
+ method public long getTotalTimeVisible();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.usage.UsageStats> CREATOR;
}
@@ -11235,6 +11273,7 @@
method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllSessions();
method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
method public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
+ method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions();
method public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback, android.os.Handler);
@@ -11273,6 +11312,7 @@
method public java.lang.String[] getNames() throws java.io.IOException;
method public int getParentSessionId();
method public boolean isMultiPackage();
+ method public boolean isStaged();
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
method public void removeChildSessionId(int);
@@ -11311,6 +11351,7 @@
method public boolean isActive();
method public boolean isMultiPackage();
method public boolean isSealed();
+ method public boolean isStaged();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
field public static final int INVALID_ID = -1; // 0xffffffff
@@ -11329,6 +11370,7 @@
method public void setOriginatingUri(android.net.Uri);
method public void setReferrerUri(android.net.Uri);
method public void setSize(long);
+ method public void setStaged();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionParams> CREATOR;
field public static final int MODE_FULL_INSTALL = 1; // 0x1
@@ -15724,6 +15766,7 @@
public static class MeasuredText.Builder {
ctor public MeasuredText.Builder(char[]);
+ ctor public MeasuredText.Builder(android.graphics.text.MeasuredText);
method public android.graphics.text.MeasuredText.Builder appendReplacementRun(android.graphics.Paint, int, float);
method public android.graphics.text.MeasuredText.Builder appendStyleRun(android.graphics.Paint, int, boolean);
method public android.graphics.text.MeasuredText build();
@@ -23061,6 +23104,7 @@
method public deprecated boolean isBluetoothA2dpOn();
method public boolean isBluetoothScoAvailableOffCall();
method public boolean isBluetoothScoOn();
+ method public static boolean isHapticPlaybackSupported();
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
method public static boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
@@ -23957,6 +24001,7 @@
method public void releaseOutputBuffer(int, boolean);
method public void releaseOutputBuffer(int, long);
method public void reset();
+ method public void setAudioPresentation(android.media.AudioPresentation);
method public void setCallback(android.media.MediaCodec.Callback, android.os.Handler);
method public void setCallback(android.media.MediaCodec.Callback);
method public void setInputSurface(android.view.Surface);
@@ -24947,6 +24992,7 @@
field public static final int MUXER_OUTPUT_3GPP = 2; // 0x2
field public static final int MUXER_OUTPUT_HEIF = 3; // 0x3
field public static final int MUXER_OUTPUT_MPEG_4 = 0; // 0x0
+ field public static final int MUXER_OUTPUT_OGG = 4; // 0x4
field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
}
@@ -25402,6 +25448,7 @@
field public static final int AMR_WB = 2; // 0x2
field public static final int DEFAULT = 0; // 0x0
field public static final int HE_AAC = 4; // 0x4
+ field public static final int OPUS = 7; // 0x7
field public static final int VORBIS = 6; // 0x6
}
@@ -25452,6 +25499,7 @@
field public static final int DEFAULT = 0; // 0x0
field public static final int MPEG_2_TS = 8; // 0x8
field public static final int MPEG_4 = 2; // 0x2
+ field public static final int OGG = 11; // 0xb
field public static final deprecated int RAW_AMR = 3; // 0x3
field public static final int THREE_GPP = 1; // 0x1
field public static final int WEBM = 9; // 0x9
@@ -35930,9 +35978,11 @@
}
public final class CalendarContract {
+ method public static boolean startViewCalendarEventInManagedProfile(android.content.Context, long, long, long, boolean, int);
field public static final java.lang.String ACCOUNT_TYPE_LOCAL = "LOCAL";
field public static final java.lang.String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER";
field public static final java.lang.String ACTION_HANDLE_CUSTOM_EVENT = "android.provider.calendar.action.HANDLE_CUSTOM_EVENT";
+ field public static final java.lang.String ACTION_VIEW_WORK_CALENDAR_EVENT = "android.provider.calendar.action.VIEW_WORK_CALENDAR_EVENT";
field public static final java.lang.String AUTHORITY = "com.android.calendar";
field public static final java.lang.String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
field public static final android.net.Uri CONTENT_URI;
@@ -35940,6 +35990,7 @@
field public static final java.lang.String EXTRA_EVENT_ALL_DAY = "allDay";
field public static final java.lang.String EXTRA_EVENT_BEGIN_TIME = "beginTime";
field public static final java.lang.String EXTRA_EVENT_END_TIME = "endTime";
+ field public static final java.lang.String EXTRA_EVENT_ID = "id";
}
public static final class CalendarContract.Attendees implements android.provider.BaseColumns android.provider.CalendarContract.AttendeesColumns android.provider.CalendarContract.EventsColumns {
@@ -37455,6 +37506,8 @@
field public static final java.lang.String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
field public static final java.lang.String EXTRA_PROMPT = "android.provider.extra.PROMPT";
field public static final java.lang.String METADATA_EXIF = "android:documentExif";
+ field public static final java.lang.String METADATA_TREE_COUNT = "android:metadataTreeCount";
+ field public static final java.lang.String METADATA_TREE_SIZE = "android:metadataTreeSize";
field public static final java.lang.String METADATA_TYPES = "android:documentMetadataTypes";
field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
field public static final java.lang.String QUERY_ARG_DISPLAY_NAME = "android:query-arg-display-name";
@@ -38468,10 +38521,13 @@
}
public static final class Telephony.CarrierId implements android.provider.BaseColumns {
+ method public static android.net.Uri getPreciseCarrierIdUriForSubscriptionId(int);
method public static android.net.Uri getUriForSubscriptionId(int);
field public static final java.lang.String CARRIER_ID = "carrier_id";
field public static final java.lang.String CARRIER_NAME = "carrier_name";
field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String PRECISE_CARRIER_ID = "precise_carrier_id";
+ field public static final java.lang.String PRECISE_CARRIER_ID_NAME = "precise_carrier_id_name";
}
public static final class Telephony.Carriers implements android.provider.BaseColumns {
@@ -40617,13 +40673,16 @@
public class CarrierIdentifier implements android.os.Parcelable {
ctor public CarrierIdentifier(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ ctor public CarrierIdentifier(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, int, int);
ctor public CarrierIdentifier(byte[], java.lang.String, java.lang.String);
method public int describeContents();
+ method public int getCarrierId();
method public java.lang.String getGid1();
method public java.lang.String getGid2();
method public java.lang.String getImsi();
method public java.lang.String getMcc();
method public java.lang.String getMnc();
+ method public int getPreciseCarrierId();
method public java.lang.String getSpn();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.carrier.CarrierIdentifier> CREATOR;
@@ -43115,12 +43174,14 @@
field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
+ field public static final java.lang.String EXTRA_IS_ENABLED = "android.telecom.extra.IS_ENABLED";
field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
field public static final java.lang.String METADATA_INCLUDE_SELF_MANAGED_CALLS = "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
+ field public static final java.lang.String METADATA_IN_CALL_SERVICE_CAR_MODE_UI = "android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
@@ -43308,6 +43369,7 @@
field public static final java.lang.String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array";
field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
field public static final java.lang.String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
+ field public static final java.lang.String KEY_CARRIER_CONFIG_VERSION_STRING = "carrier_config_version_string";
field public static final java.lang.String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = "carrier_data_call_permanent_failure_strings";
field public static final java.lang.String KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = "carrier_default_wfc_ims_mode_int";
field public static final java.lang.String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = "carrier_default_wfc_ims_roaming_mode_int";
@@ -44105,12 +44167,14 @@
method public static int getSlotIndex(int);
method public int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
+ method public java.util.List<android.telephony.SubscriptionInfo> getSubscriptionsInGroup(int);
method public boolean isActiveSubscriptionId(int);
method public boolean isNetworkRoaming(int);
method public static boolean isUsableSubscriptionId(int);
method public static boolean isValidSubscriptionId(int);
method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+ method public boolean removeSubscriptionsFromGroup(int[]);
method public java.lang.String setSubscriptionGroup(int[]);
method public void setSubscriptionOverrideCongested(int, boolean, long);
method public void setSubscriptionOverrideUnmetered(int, boolean, long);
@@ -44175,6 +44239,7 @@
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
method public android.os.PersistableBundle getCarrierConfig();
+ method public int getCarrierIdFromSimMccMnc();
method public deprecated android.telephony.CellLocation getCellLocation();
method public java.util.Map<java.lang.Integer, java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList();
method public java.util.Map<java.lang.Integer, java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(int);
@@ -44212,6 +44277,8 @@
method public java.lang.String getSimCountryIso();
method public java.lang.String getSimOperator();
method public java.lang.String getSimOperatorName();
+ method public int getSimPreciseCarrierId();
+ method public java.lang.CharSequence getSimPreciseCarrierIdName();
method public java.lang.String getSimSerialNumber();
method public int getSimState();
method public int getSimState(int);
@@ -44268,6 +44335,7 @@
field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
field public static final java.lang.String ACTION_SMS_APP_SERVICE = "android.telephony.action.SMS_APP_SERVICE";
field public static final java.lang.String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
+ field public static final java.lang.String ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED";
field public static final int APPTYPE_CSIM = 4; // 0x4
field public static final int APPTYPE_ISIM = 5; // 0x5
field public static final int APPTYPE_RUIM = 3; // 0x3
@@ -44300,6 +44368,8 @@
field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
+ field public static final java.lang.String EXTRA_PRECISE_CARRIER_ID = "android.telephony.extra.PRECISE_CARRIER_ID";
+ field public static final java.lang.String EXTRA_PRECISE_CARRIER_NAME = "android.telephony.extra.PRECISE_CARRIER_NAME";
field public static final java.lang.String EXTRA_STATE = "state";
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
@@ -45262,6 +45332,7 @@
public static class PrecomputedText.Params.Builder {
ctor public PrecomputedText.Params.Builder(android.text.TextPaint);
+ ctor public PrecomputedText.Params.Builder(android.text.PrecomputedText.Params);
method public android.text.PrecomputedText.Params build();
method public android.text.PrecomputedText.Params.Builder setBreakStrategy(int);
method public android.text.PrecomputedText.Params.Builder setHyphenationFrequency(int);
@@ -52493,19 +52564,19 @@
method public int describeContents();
method public android.app.Person getAuthor();
method public android.os.Bundle getExtras();
+ method public java.time.ZonedDateTime getReferenceTime();
method public java.lang.CharSequence getText();
- 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;
+ field public static final android.app.Person PERSON_USER_REMOTE;
}
public static final class ConversationActions.Message.Builder {
- ctor public ConversationActions.Message.Builder();
+ ctor public ConversationActions.Message.Builder(android.app.Person);
method public android.view.textclassifier.ConversationActions.Message build();
- method public android.view.textclassifier.ConversationActions.Message.Builder setAuthor(android.app.Person);
- method public android.view.textclassifier.ConversationActions.Message.Builder setComposeTime(java.time.ZonedDateTime);
method public android.view.textclassifier.ConversationActions.Message.Builder setExtras(android.os.Bundle);
+ method public android.view.textclassifier.ConversationActions.Message.Builder setReferenceTime(java.time.ZonedDateTime);
method public android.view.textclassifier.ConversationActions.Message.Builder setText(java.lang.CharSequence);
}
@@ -52688,6 +52759,7 @@
method public default android.view.textclassifier.ConversationActions suggestConversationActions(android.view.textclassifier.ConversationActions.Request);
method public default android.view.textclassifier.TextSelection suggestSelection(android.view.textclassifier.TextSelection.Request);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
+ field public static final java.lang.String EXTRA_FROM_TEXT_CLASSIFIER = "android.view.textclassifier.extra.FROM_TEXT_CLASSIFIER";
field public static final java.lang.String HINT_TEXT_IS_EDITABLE = "android.text_is_editable";
field public static final java.lang.String HINT_TEXT_IS_NOT_EDITABLE = "android.text_is_not_editable";
field public static final android.view.textclassifier.TextClassifier NO_OP;
diff --git a/api/removed.txt b/api/removed.txt
index f7106d2..e3e8b63 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -508,6 +508,21 @@
field public static final deprecated java.lang.String TIMESTAMP = "timestamp";
}
+ public final class DocumentsContract {
+ method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
+ method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method public static boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ }
+
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
field public static final deprecated java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index eecbf21..09dca1e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4,7 +4,6 @@
field public static final java.lang.String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS";
field public static final java.lang.String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
field public static final java.lang.String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
- field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
field public static final java.lang.String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
field public static final deprecated java.lang.String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
field public static final java.lang.String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
@@ -14,14 +13,11 @@
field public static final java.lang.String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final java.lang.String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
- field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
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";
field public static final java.lang.String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
field public static final java.lang.String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
@@ -32,7 +28,6 @@
field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
field public static final java.lang.String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
- field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
@@ -42,18 +37,13 @@
field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
field public static final java.lang.String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final java.lang.String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
- field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED";
field public static final java.lang.String BRICK = "android.permission.BRICK";
field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final deprecated java.lang.String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED";
- field public static final java.lang.String CALL_PRIVILEGED = "android.permission.CALL_PRIVILEGED";
field public static final java.lang.String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
field public static final java.lang.String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
- field public static final java.lang.String CAPTURE_AUDIO_OUTPUT = "android.permission.CAPTURE_AUDIO_OUTPUT";
field public static final java.lang.String CAPTURE_TV_INPUT = "android.permission.CAPTURE_TV_INPUT";
field public static final java.lang.String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
- field public static final java.lang.String CHANGE_COMPONENT_ENABLED_STATE = "android.permission.CHANGE_COMPONENT_ENABLED_STATE";
- field public static final java.lang.String CHANGE_CONFIGURATION = "android.permission.CHANGE_CONFIGURATION";
field public static final java.lang.String CHANGE_DEVICE_IDLE_TEMP_WHITELIST = "android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST";
field public static final java.lang.String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
@@ -63,22 +53,15 @@
field public static final java.lang.String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
field public static final java.lang.String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
field public static final java.lang.String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS";
- field public static final java.lang.String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES";
field public static final java.lang.String CONTROL_VPN = "android.permission.CONTROL_VPN";
field public static final java.lang.String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
- field public static final java.lang.String DELETE_CACHE_FILES = "android.permission.DELETE_CACHE_FILES";
- field public static final java.lang.String DELETE_PACKAGES = "android.permission.DELETE_PACKAGES";
field public static final java.lang.String DEVICE_POWER = "android.permission.DEVICE_POWER";
- field public static final java.lang.String DIAGNOSTIC = "android.permission.DIAGNOSTIC";
field public static final java.lang.String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
- field public static final java.lang.String DUMP = "android.permission.DUMP";
field public static final java.lang.String FORCE_BACK = "android.permission.FORCE_BACK";
field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
- field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
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";
@@ -86,8 +69,6 @@
field public static final java.lang.String HIDE_NON_SYSTEM_OVERLAY_WINDOWS = "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS";
field public static final java.lang.String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
field public static final java.lang.String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS";
- field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
- field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
field public static final java.lang.String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
field public static final java.lang.String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES";
field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
@@ -98,7 +79,6 @@
field public static final java.lang.String INVOKE_CARRIER_SETUP = "android.permission.INVOKE_CARRIER_SETUP";
field public static final java.lang.String KILL_UID = "android.permission.KILL_UID";
field public static final java.lang.String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
- field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
field public static final java.lang.String MANAGE_ACCESSIBILITY = "android.permission.MANAGE_ACCESSIBILITY";
@@ -118,26 +98,21 @@
field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final java.lang.String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
- field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
- field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final java.lang.String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS";
field public static final java.lang.String MODIFY_AUDIO_ROUTING = "android.permission.MODIFY_AUDIO_ROUTING";
field public static final java.lang.String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
field public static final java.lang.String MODIFY_DAY_NIGHT_MODE = "android.permission.MODIFY_DAY_NIGHT_MODE";
field public static final deprecated java.lang.String MODIFY_NETWORK_ACCOUNTING = "android.permission.MODIFY_NETWORK_ACCOUNTING";
field public static final java.lang.String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
- field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
field public static final java.lang.String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
- field public static final java.lang.String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
- field public static final java.lang.String MOUNT_UNMOUNT_FILESYSTEMS = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS";
field public static final java.lang.String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
field public static final java.lang.String NETWORK_MANAGED_PROVISIONING = "android.permission.NETWORK_MANAGED_PROVISIONING";
field public static final java.lang.String NETWORK_SETUP_WIZARD = "android.permission.NETWORK_SETUP_WIZARD";
field public static final java.lang.String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
field public static final java.lang.String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
field public static final java.lang.String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
+ field public static final java.lang.String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
field public static final java.lang.String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
- field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
field public static final java.lang.String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
field public static final java.lang.String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS";
field public static final java.lang.String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING";
@@ -150,7 +125,6 @@
field public static final java.lang.String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final java.lang.String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
field public static final java.lang.String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
- field public static final java.lang.String READ_LOGS = "android.permission.READ_LOGS";
field public static final java.lang.String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY";
field public static final java.lang.String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE";
field public static final java.lang.String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES";
@@ -162,7 +136,6 @@
field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
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";
@@ -173,33 +146,26 @@
field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
+ field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
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";
field public static final java.lang.String SERIAL_PORT = "android.permission.SERIAL_PORT";
field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
- field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
- field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
- field public static final java.lang.String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
field public static final java.lang.String SET_HARMFUL_APP_WARNINGS = "android.permission.SET_HARMFUL_APP_WARNINGS";
field public static final java.lang.String SET_MEDIA_KEY_LISTENER = "android.permission.SET_MEDIA_KEY_LISTENER";
field public static final java.lang.String SET_ORIENTATION = "android.permission.SET_ORIENTATION";
field public static final java.lang.String SET_POINTER_SPEED = "android.permission.SET_POINTER_SPEED";
- field public static final java.lang.String SET_PROCESS_LIMIT = "android.permission.SET_PROCESS_LIMIT";
field public static final java.lang.String SET_SCREEN_COMPATIBILITY = "android.permission.SET_SCREEN_COMPATIBILITY";
- field public static final java.lang.String SET_TIME = "android.permission.SET_TIME";
field public static final java.lang.String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
field public static final java.lang.String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
field public static final java.lang.String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
field public static final java.lang.String SHUTDOWN = "android.permission.SHUTDOWN";
- field public static final java.lang.String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
- field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
field public static final java.lang.String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
field public static final java.lang.String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
field public static final java.lang.String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
@@ -208,18 +174,14 @@
field public static final java.lang.String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
field public static final java.lang.String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
field public static final java.lang.String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS";
- field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
field public static final java.lang.String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
field public static final java.lang.String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
field public static final java.lang.String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
field public static final java.lang.String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
- field public static final java.lang.String WRITE_APN_SETTINGS = "android.permission.WRITE_APN_SETTINGS";
field public static final java.lang.String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
field public static final java.lang.String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
- field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES";
field public static final java.lang.String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final java.lang.String WRITE_OBB = "android.permission.WRITE_OBB";
- field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
}
public static final class Manifest.permission_group {
@@ -580,10 +542,6 @@
package android.app.admin {
- public class DeviceAdminReceiver extends android.content.BroadcastReceiver {
- method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent);
- }
-
public class DevicePolicyManager {
method public java.lang.String getDeviceOwner();
method public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
@@ -632,10 +590,7 @@
}
public final class SystemUpdatePolicy implements android.os.Parcelable {
- method public int describeContents();
method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR;
field public static final int TYPE_PAUSE = 4; // 0x4
}
@@ -887,12 +842,18 @@
package android.app.role {
+ public abstract interface OnRoleHoldersChangedListener {
+ method public abstract void onRoleHoldersChanged(java.lang.String, android.os.UserHandle);
+ }
+
public final class RoleManager {
+ method public void addOnRoleHoldersChangedListenerAsUser(java.util.concurrent.Executor, android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle);
method public void addRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public boolean addRoleHolderFromController(java.lang.String, java.lang.String);
method public void clearRoleHoldersAsUser(java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public java.util.List<java.lang.String> getRoleHolders(java.lang.String);
method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
+ method public void removeOnRoleHoldersChangedListenerAsUser(android.app.role.OnRoleHoldersChangedListener, 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>);
@@ -938,6 +899,7 @@
}
public static final class UsageEvents.Event {
+ method public int getInstanceId();
method public java.lang.String getNotificationChannelId();
field public static final int NOTIFICATION_INTERRUPTION = 12; // 0xc
field public static final int NOTIFICATION_SEEN = 10; // 0xa
@@ -1046,7 +1008,7 @@
method public void setDetectNotResponding(long);
}
- public abstract class ContentResolver {
+ public abstract class ContentResolver implements android.content.ContentInterface {
method public android.graphics.drawable.Drawable getTypeDrawable(java.lang.String);
}
@@ -1253,6 +1215,7 @@
method public abstract boolean arePermissionsIndividuallyControlled();
method public boolean canSuspendPackage(java.lang.String);
method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
+ method public android.content.pm.ApplicationInfo getApplicationInfoAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.dex.ArtManager getArtManager();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public java.lang.CharSequence getHarmfulAppWarning(java.lang.String);
@@ -1264,11 +1227,13 @@
method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
- method public java.lang.String getWellbeingPackageName();
method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
+ method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(android.content.Intent, int, android.os.UserHandle);
+ method public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(android.content.Intent, int, android.os.UserHandle);
+ method public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(android.content.Intent, int, android.os.UserHandle);
method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public deprecated void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
@@ -1365,6 +1330,7 @@
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
field public static final int FLAG_REMOVED = 2; // 0x2
+ field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
@@ -3242,7 +3208,6 @@
method public void addBlockedRating(android.media.tv.TvContentRating);
method public boolean captureFrame(java.lang.String, android.view.Surface, android.media.tv.TvStreamConfig);
method public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(java.lang.String);
- method public java.util.List<android.media.tv.TvContentRating> getBlockedRatings();
method public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
method public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
method public boolean isSingleSessionActive();
@@ -3488,11 +3453,9 @@
}
public class TrafficStats {
- method public static void clearThreadStatsUid();
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();
method public static void setThreadStatsTagRestore();
- method public static void setThreadStatsUid(int);
}
public class VpnService extends android.app.Service {
@@ -3514,14 +3477,6 @@
}
-package android.net.http {
-
- public class X509TrustManagerExtensions {
- method public boolean isSameTrustConfiguration(java.lang.String, java.lang.String);
- }
-
-}
-
package android.net.wifi {
public deprecated class RttManager {
@@ -3745,7 +3700,6 @@
method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method public int getWifiApState();
- method public boolean isDeviceToApRttSupported();
method public boolean isDeviceToDeviceRttSupported();
method public boolean isPortableHotspotSupported();
method public boolean isWifiApEnabled();
@@ -5196,28 +5150,6 @@
field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
}
- public final class Condition implements android.os.Parcelable {
- ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
- method public android.service.notification.Condition copy();
- method public static boolean isValidId(android.net.Uri, java.lang.String);
- method public static android.net.Uri.Builder newId(android.content.Context);
- method public static java.lang.String relevanceToString(int);
- method public static java.lang.String stateToString(int);
- field public static final int FLAG_RELEVANT_ALWAYS = 2; // 0x2
- field public static final int FLAG_RELEVANT_NOW = 1; // 0x1
- field public static final java.lang.String SCHEME = "condition";
- field public static final int STATE_ERROR = 3; // 0x3
- field public static final int STATE_UNKNOWN = 2; // 0x2
- field public final int flags;
- field public final int icon;
- field public final java.lang.String line1;
- field public final java.lang.String line2;
- }
-
- public abstract class ConditionProviderService extends android.app.Service {
- method public void onRequestConditions(int);
- }
-
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
method public final void adjustNotification(android.service.notification.Adjustment);
@@ -5700,7 +5632,6 @@
method public void clearPhoneAccounts();
method public android.telecom.TelecomAnalytics dumpAnalytics();
method public void enablePhoneAccount(android.telecom.PhoneAccountHandle, boolean);
- method public boolean endCall();
method public java.util.List<android.telecom.PhoneAccountHandle> getAllPhoneAccountHandles();
method public java.util.List<android.telecom.PhoneAccount> getAllPhoneAccounts();
method public int getAllPhoneAccountsCount();
@@ -5712,7 +5643,6 @@
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
method public boolean isInEmergencyCall();
method public boolean isRinging();
- method public boolean isTtySupported();
method public boolean setDefaultDialer(java.lang.String);
field public static final java.lang.String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
field public static final java.lang.String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
@@ -5737,7 +5667,6 @@
method public void overrideConfig(int, android.os.PersistableBundle);
method public void updateConfigForPhoneId(int, java.lang.String);
field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
- field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
}
public class MbmsDownloadSession implements java.lang.AutoCloseable {
@@ -5852,7 +5781,6 @@
public final class SmsManager {
method public void sendMultipartTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
- method public void sendTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
field public static final int RESULT_CANCELLED = 23; // 0x17
field public static final int RESULT_ENCODING_ERROR = 18; // 0x12
field public static final int RESULT_ERROR_FDN_CHECK_FAILURE = 6; // 0x6
@@ -5879,15 +5807,9 @@
public class SubscriptionManager {
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
- method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public void requestEmbeddedSubscriptionInfoListRefresh();
method public void setDefaultDataSubId(int);
method public void setDefaultSmsSubId(int);
- method public void setSubscriptionOverrideCongested(int, boolean, long);
- method public void setSubscriptionOverrideUnmetered(int, boolean, long);
- method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
- field public static final java.lang.String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
- field public static final java.lang.String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
field public static final android.net.Uri VT_ENABLED_CONTENT_URI;
field public static final android.net.Uri WFC_ENABLED_CONTENT_URI;
@@ -5896,37 +5818,10 @@
field public static final android.net.Uri WFC_ROAMING_MODE_CONTENT_URI;
}
- public final class SubscriptionPlan implements android.os.Parcelable {
- method public java.util.Iterator<android.util.Range<java.time.ZonedDateTime>> cycleIterator();
- method public int describeContents();
- method public int getDataLimitBehavior();
- method public long getDataLimitBytes();
- method public long getDataUsageBytes();
- method public long getDataUsageTime();
- method public java.lang.CharSequence getSummary();
- method public java.lang.CharSequence getTitle();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final long BYTES_UNKNOWN = -1L; // 0xffffffffffffffffL
- field public static final long BYTES_UNLIMITED = 9223372036854775807L; // 0x7fffffffffffffffL
- field public static final android.os.Parcelable.Creator<android.telephony.SubscriptionPlan> CREATOR;
- field public static final int LIMIT_BEHAVIOR_BILLED = 1; // 0x1
- field public static final int LIMIT_BEHAVIOR_DISABLED = 0; // 0x0
- field public static final int LIMIT_BEHAVIOR_THROTTLED = 2; // 0x2
- field public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; // 0xffffffff
- field public static final long TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
- }
-
public static class SubscriptionPlan.Builder {
- method public android.telephony.SubscriptionPlan build();
- method public static android.telephony.SubscriptionPlan.Builder createNonrecurring(java.time.ZonedDateTime, java.time.ZonedDateTime);
- method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringDaily(java.time.ZonedDateTime);
method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringMonthly(java.time.ZonedDateTime);
method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
- method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
- method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
- method public android.telephony.SubscriptionPlan.Builder setSummary(java.lang.CharSequence);
- method public android.telephony.SubscriptionPlan.Builder setTitle(java.lang.CharSequence);
}
public final class TelephonyHistogram implements android.os.Parcelable {
@@ -6846,12 +6741,6 @@
method public android.telephony.ims.ImsSsInfo.Builder setProvisionStatus(int);
}
- public static abstract class ImsSsInfo.ClirInterrogationStatus implements java.lang.annotation.Annotation {
- }
-
- public static abstract class ImsSsInfo.ClirOutgoingState implements java.lang.annotation.Annotation {
- }
-
public final class ImsStreamMediaProfile implements android.os.Parcelable {
ctor public ImsStreamMediaProfile(int, int, int, int, int);
method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
@@ -7351,6 +7240,7 @@
package android.view.accessibility {
public final class AccessibilityManager {
+ method public int getAccessibilityWindowId(android.os.IBinder);
method public void performAccessibilityShortcut();
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 22e8005..627ef22 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5,11 +5,12 @@
field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final java.lang.String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
- field public static final java.lang.String CHANGE_CONFIGURATION = "android.permission.CHANGE_CONFIGURATION";
field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
+ field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+ field public static final java.lang.String WRITE_OBB = "android.permission.WRITE_OBB";
}
}
@@ -361,6 +362,7 @@
method public abstract java.lang.String getPermissionControllerPackageName();
method public abstract java.lang.String getServicesSystemSharedLibraryPackageName();
method public abstract java.lang.String getSharedSystemSharedLibraryPackageName();
+ method public java.lang.String getWellbeingPackageName();
method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
field public static final java.lang.String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
@@ -372,6 +374,7 @@
}
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
@@ -1707,11 +1710,6 @@
method public static int getLongPressTooltipHideTimeout();
}
- public final class ViewTreeObserver {
- method public void registerFrameCommitCallback(java.lang.Runnable);
- method public boolean unregisterFrameCommitCallback(java.lang.Runnable);
- }
-
public abstract interface WindowManager implements android.view.ViewManager {
method public default void setShouldShowIme(int, boolean);
method public default void setShouldShowSystemDecors(int, boolean);
diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
index f92d813..573ca4f 100644
--- a/cmds/incident_helper/tests/KernelWakesParser_test.cpp
+++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
@@ -84,9 +84,9 @@
record1->set_event_count(8);
record1->set_wakeup_count(0);
record1->set_expire_count(0);
- record1->set_active_since(0l);
- record1->set_total_time(0l);
- record1->set_max_time(0l);
+ record1->set_active_since(0L);
+ record1->set_total_time(0L);
+ record1->set_max_time(0L);
record1->set_last_change(131348LL);
record1->set_prevent_suspend_time(0LL);
@@ -96,9 +96,9 @@
record2->set_event_count(143);
record2->set_wakeup_count(0);
record2->set_expire_count(0);
- record2->set_active_since(0l);
- record2->set_total_time(123l);
- record2->set_max_time(3l);
+ record2->set_active_since(0L);
+ record2->set_total_time(123L);
+ record2->set_max_time(3L);
record2->set_last_change(2067286206LL);
record2->set_prevent_suspend_time(0LL);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 7fa05be..04173b2 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -424,13 +424,14 @@
dprintf(out, "\n be removed from memory and disk!\n");
dprintf(out, "\n");
dprintf(out,
- "usage: adb shell cmd stats dump-report [UID] NAME [--include_current_bucket] "
- "[--proto]\n");
+ "usage: adb shell cmd stats dump-report [UID] NAME [--keep_data] "
+ "[--include_current_bucket] [--proto]\n");
dprintf(out, " Dump all metric data for a configuration.\n");
dprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
dprintf(out, " calling uid is used.\n");
dprintf(out, " NAME The name of the configuration\n");
+ dprintf(out, " --keep_data Do NOT erase the data upon dumping it.\n");
dprintf(out, " --proto Print proto binary.\n");
dprintf(out, "\n");
dprintf(out, "\n");
@@ -590,6 +591,7 @@
bool good = false;
bool proto = false;
bool includeCurrentBucket = false;
+ bool eraseData = true;
int uid;
string name;
if (!std::strcmp("--proto", args[argCount-1].c_str())) {
@@ -600,6 +602,10 @@
includeCurrentBucket = true;
argCount -= 1;
}
+ if (!std::strcmp("--keep_data", args[argCount-1].c_str())) {
+ eraseData = false;
+ argCount -= 1;
+ }
if (argCount == 2) {
// Automatically pick the UID
uid = IPCThreadState::self()->getCallingUid();
@@ -627,7 +633,7 @@
if (good) {
vector<uint8_t> data;
mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
- includeCurrentBucket, true /* erase_data */, ADB_DUMP, &data);
+ includeCurrentBucket, eraseData, ADB_DUMP, &data);
if (proto) {
for (size_t i = 0; i < data.size(); i ++) {
dprintf(out, "%c", data[i]);
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 355df29..237f8b9 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -240,6 +240,49 @@
EXPECT_EQ(2, report.annotation(0).field_int32());
}
+TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
+ // Setup a simple config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = wakelockAcquireMatcher;
+
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(123456);
+ countMetric->set_what(wakelockAcquireMatcher.id());
+ countMetric->set_bucket(FIVE_MINUTES);
+
+ ConfigKey cfgKey;
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);
+
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+ auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 2);
+ processor->OnLogEvent(event.get());
+
+ vector<uint8_t> bytes;
+ ConfigMetricsReportList output;
+
+ // Dump report WITHOUT erasing data.
+ processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, &bytes);
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).metrics_size(), 1);
+ EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
+
+ // Dump report WITH erasing data. There should be data since we didn't previously erase it.
+ processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, &bytes);
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).metrics_size(), 1);
+ EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
+
+ // Dump report again. There should be no data since we erased it.
+ processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, &bytes);
+ output.ParseFromArray(bytes.data(), bytes.size());
+ bool noData = (output.reports_size() == 0) || (output.reports(0).metrics_size() == 0);
+ EXPECT_TRUE(noData);
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tools/localtools/Android.bp b/cmds/statsd/tools/localtools/Android.bp
new file mode 100644
index 0000000..75a57a3
--- /dev/null
+++ b/cmds/statsd/tools/localtools/Android.bp
@@ -0,0 +1,25 @@
+java_binary_host {
+ name: "statsd_localdrive",
+ manifest: "localdrive_manifest.txt",
+ srcs: [
+ "src/com/android/statsd/shelltools/localdrive/*.java",
+ "src/com/android/statsd/shelltools/Utils.java",
+ ],
+ static_libs: [
+ "platformprotos",
+ "guava",
+ ],
+}
+
+java_binary_host {
+ name: "statsd_testdrive",
+ manifest: "testdrive_manifest.txt",
+ srcs: [
+ "src/com/android/statsd/shelltools/testdrive/*.java",
+ "src/com/android/statsd/shelltools/Utils.java",
+ ],
+ static_libs: [
+ "platformprotos",
+ "guava",
+ ],
+}
\ No newline at end of file
diff --git a/cmds/statsd/tools/localtools/localdrive_manifest.txt b/cmds/statsd/tools/localtools/localdrive_manifest.txt
new file mode 100644
index 0000000..035cea1
--- /dev/null
+++ b/cmds/statsd/tools/localtools/localdrive_manifest.txt
@@ -0,0 +1 @@
+Main-class: com.android.statsd.shelltools.localdrive.LocalDrive
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
new file mode 100644
index 0000000..597377e
--- /dev/null
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.statsd.shelltools;
+
+import com.android.os.StatsLog.ConfigMetricsReportList;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Formatter;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+/**
+ * Utilities for local use of statsd.
+ */
+public class Utils {
+
+ public static final String CMD_UPDATE_CONFIG = "cmd stats config update";
+ public static final String CMD_DUMP_REPORT = "cmd stats dump-report";
+ public static final String CMD_REMOVE_CONFIG = "cmd stats config remove";
+
+ public static final String SHELL_UID = "2000"; // Use shell, even if rooted.
+
+ /**
+ * Runs adb shell command with output directed to outputFile if non-null.
+ */
+ public static void runCommand(File outputFile, Logger logger, String... commands)
+ throws IOException, InterruptedException {
+ ProcessBuilder pb = new ProcessBuilder(commands);
+ if (outputFile != null && outputFile.exists() && outputFile.canWrite()) {
+ pb.redirectOutput(outputFile);
+ }
+ Process process = pb.start();
+
+ // Capture any errors
+ StringBuilder err = new StringBuilder();
+ BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+ for (String line = br.readLine(); line != null; line = br.readLine()) {
+ err.append(line).append('\n');
+ }
+ logger.severe(err.toString());
+
+ // Check result
+ if (process.waitFor() == 0) {
+ logger.fine("Adb command successful.");
+ } else {
+ logger.severe("Abnormal adb shell cmd termination for: " + String.join(",", commands));
+ throw new RuntimeException("Error running adb command: " + err.toString());
+ }
+ }
+
+ /**
+ * Dumps the report from the device and converts it to a ConfigMetricsReportList.
+ * Erases the data if clearData is true.
+ */
+ public static ConfigMetricsReportList getReportList(long configId, boolean clearData,
+ Logger logger) throws IOException, InterruptedException {
+ try {
+ File outputFile = File.createTempFile("statsdret", ".bin");
+ outputFile.deleteOnExit();
+ runCommand(
+ outputFile,
+ logger,
+ "adb",
+ "shell",
+ CMD_DUMP_REPORT,
+ SHELL_UID,
+ String.valueOf(configId),
+ clearData ? "" : "--keep_data",
+ "--include_current_bucket",
+ "--proto");
+ ConfigMetricsReportList reportList =
+ ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile));
+ return reportList;
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ logger.severe("Failed to fetch and parse the statsd output report. "
+ + "Perhaps there is not a valid statsd config for the requested "
+ + "uid=" + SHELL_UID
+ + ", configId=" + configId
+ + ".");
+ throw (e);
+ }
+ }
+
+ public static void setUpLogger(Logger logger, boolean debug) {
+ ConsoleHandler handler = new ConsoleHandler();
+ handler.setFormatter(new LocalToolsFormatter());
+ logger.setUseParentHandlers(false);
+ if (debug) {
+ handler.setLevel(Level.ALL);
+ logger.setLevel(Level.ALL);
+ }
+ logger.addHandler(handler);
+ }
+
+ public static class LocalToolsFormatter extends Formatter {
+ public String format(LogRecord record) {
+ return record.getMessage() + "\n";
+ }
+ }
+}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
new file mode 100644
index 0000000..08074ed
--- /dev/null
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.statsd.shelltools.localdrive;
+
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.statsd.shelltools.Utils;
+
+import com.google.common.io.Files;
+import com.google.protobuf.TextFormat;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/**
+ * Tool for using statsd locally. Can upload a config and get the data. Handles
+ * both binary and human-readable protos.
+ * To make: make statsd_localdrive
+ * To run: statsd_localdrive (i.e. ./out/host/linux-x86/bin/statsd_localdrive)
+ */
+public class LocalDrive {
+ private static final boolean DEBUG = false;
+
+ public static final long DEFAULT_CONFIG_ID = 56789;
+
+ public static final String BINARY_FLAG = "--binary";
+ public static final String CLEAR_DATA = "--clear";
+ public static final String NO_UID_MAP_FLAG = "--no-uid-map";
+
+ public static final String HELP_STRING =
+ "Usage:\n\n" +
+
+ "statsd_local upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ " Uploads the given statsd config file (in binary or human-readable-text format).\n" +
+ " If a config with this id already exists, removes it first.\n" +
+ " CONFIG_FILE Location of config file on host.\n" +
+ " CONFIG_ID Long ID to associate with this config. If absent, uses "
+ + DEFAULT_CONFIG_ID + ".\n" +
+ " --binary Config is in binary format; otherwise, assumed human-readable text.\n" +
+ // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
+ "\n" +
+
+ "statsd_local update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ " Same as upload, but does not remove the old config first (if it already exists).\n" +
+ // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
+ "\n" +
+
+ "statsd_local get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
+ " Prints the output statslog data (in binary or human-readable-text format).\n" +
+ " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
+ " --binary Output should be in binary, instead of default human-readable text.\n" +
+ " Binary output can be redirected as usual (e.g. > FILENAME).\n" +
+ " --no-uid-map Do not include the uid-map (the very lengthy uid<-->pkgName map).\n" +
+ " --clear Erase the data from statsd afterwards. Does not remove the config.\n" +
+ // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID [--keep_data]
+ // --include_current_bucket --proto
+ "\n" +
+
+ "statsd_local remove [CONFIG_ID]\n" +
+ " Removes the config.\n" +
+ " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
+ // Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID
+ "\n" +
+
+ "statsd_local clear [CONFIG_ID]\n" +
+ " Clears the data associated with the config.\n" +
+ " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
+ // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID
+ // --include_current_bucket --proto
+ "";
+
+
+ private static final Logger sLogger = Logger.getLogger(LocalDrive.class.getName());
+
+ /** Usage: make statsd_localdrive && statsd_localdrive */
+ public static void main(String[] args) {
+ Utils.setUpLogger(sLogger, DEBUG);
+
+ if (args.length > 0) {
+ switch (args[0]) {
+ case "clear":
+ cmdClear(args);
+ return;
+ case "get-data":
+ cmdGetData(args);
+ return;
+ case "remove":
+ cmdRemove(args);
+ return;
+ case "update":
+ cmdUpdate(args);
+ return;
+ case "upload":
+ cmdUpload(args);
+ return;
+ }
+ }
+ printHelp();
+ }
+
+ private static void printHelp() {
+ sLogger.info(HELP_STRING);
+ }
+
+ // upload CONFIG_FILE [CONFIG_ID] [--binary]
+ private static boolean cmdUpload(String[] args) {
+ return updateConfig(args, true);
+ }
+
+ // update CONFIG_FILE [CONFIG_ID] [--binary]
+ private static boolean cmdUpdate(String[] args) {
+ return updateConfig(args, false);
+ }
+
+ private static boolean updateConfig(String[] args, boolean removeOldConfig) {
+ int argCount = args.length - 1; // Used up one for upload/update.
+
+ // Get CONFIG_FILE
+ if (argCount < 1) {
+ sLogger.severe("No config file provided.");
+ printHelp();
+ return false;
+ }
+ final String origConfigLocation = args[1];
+ if (!new File(origConfigLocation).exists()) {
+ sLogger.severe("Error - Cannot find the provided config file: " + origConfigLocation);
+ return false;
+ }
+ argCount--;
+
+ // Get --binary
+ boolean binary = contains(args, 2, BINARY_FLAG);
+ if (binary) argCount --;
+
+ // Get CONFIG_ID
+ long configId;
+ try {
+ configId = getConfigId(argCount < 1, args, 2);
+ } catch (NumberFormatException e) {
+ sLogger.severe("Invalid config id provided.");
+ printHelp();
+ return false;
+ }
+ sLogger.fine(String.format("updateConfig with %s %d %b %b",
+ origConfigLocation, configId, binary, removeOldConfig));
+
+ // Remove the old config.
+ if (removeOldConfig) {
+ try {
+ Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG,
+ Utils.SHELL_UID, String.valueOf(configId));
+ Utils.getReportList(configId, true /* clearData */, sLogger);
+ } catch (InterruptedException | IOException e) {
+ sLogger.severe("Failed to remove config: " + e.getMessage());
+ return false;
+ }
+ }
+
+ // Upload the config.
+ String configLocation;
+ if (binary) {
+ configLocation = origConfigLocation;
+ } else {
+ StatsdConfig.Builder builder = StatsdConfig.newBuilder();
+ try {
+ TextFormat.merge(new FileReader(origConfigLocation), builder);
+ } catch (IOException e) {
+ sLogger.severe("Failed to read config file " + origConfigLocation + ": "
+ + e.getMessage());
+ return false;
+ }
+
+ try {
+ File tempConfigFile = File.createTempFile("statsdconfig", ".config");
+ tempConfigFile.deleteOnExit();
+ Files.write(builder.build().toByteArray(), tempConfigFile);
+ configLocation = tempConfigFile.getAbsolutePath();
+ } catch (IOException e) {
+ sLogger.severe("Failed to write temp config file: " + e.getMessage());
+ return false;
+ }
+ }
+ String remotePath = "/data/local/tmp/statsdconfig.config";
+ try {
+ Utils.runCommand(null, sLogger, "adb", "push", configLocation, remotePath);
+ Utils.runCommand(null, sLogger, "adb", "shell", "cat", remotePath, "|",
+ Utils.CMD_UPDATE_CONFIG, Utils.SHELL_UID, String.valueOf(configId));
+ } catch (InterruptedException | IOException e) {
+ sLogger.severe("Failed to update config: " + e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ // get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]
+ private static boolean cmdGetData(String[] args) {
+ boolean binary = contains(args, 1, BINARY_FLAG);
+ boolean noUidMap = contains(args, 1, NO_UID_MAP_FLAG);
+ boolean clearData = contains(args, 1, CLEAR_DATA);
+
+ // Get CONFIG_ID
+ int argCount = args.length - 1; // Used up one for get-data.
+ if (binary) argCount--;
+ if (noUidMap) argCount--;
+ if (clearData) argCount--;
+ long configId;
+ try {
+ configId = getConfigId(argCount < 1, args, 1);
+ } catch (NumberFormatException e) {
+ sLogger.severe("Invalid config id provided.");
+ printHelp();
+ return false;
+ }
+ sLogger.fine(String.format("cmdGetData with %d %b %b %b",
+ configId, clearData, binary, noUidMap));
+
+ // Get the StatsLog
+ // Even if the args request no modifications, we still parse it to make sure it's valid.
+ ConfigMetricsReportList reportList;
+ try {
+ reportList = Utils.getReportList(configId, clearData, sLogger);
+ } catch (IOException | InterruptedException e) {
+ sLogger.severe("Failed to get report list: " + e.getMessage());
+ return false;
+ }
+ if (noUidMap) {
+ ConfigMetricsReportList.Builder builder
+ = ConfigMetricsReportList.newBuilder(reportList);
+ // Clear the reports, then add them back without their UidMap.
+ builder.clearReports();
+ for (ConfigMetricsReport report : reportList.getReportsList()) {
+ builder.addReports(ConfigMetricsReport.newBuilder(report).clearUidMap());
+ }
+ reportList = builder.build();
+ }
+
+ if (!binary) {
+ sLogger.info(reportList.toString());
+ } else {
+ try {
+ System.out.write(reportList.toByteArray());
+ } catch (IOException e) {
+ sLogger.severe("Failed to output binary statslog proto: "
+ + e.getMessage());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // clear [CONFIG_ID]
+ private static boolean cmdClear(String[] args) {
+ // Get CONFIG_ID
+ long configId;
+ try {
+ configId = getConfigId(false, args, 1);
+ } catch (NumberFormatException e) {
+ sLogger.severe("Invalid config id provided.");
+ printHelp();
+ return false;
+ }
+ sLogger.fine(String.format("cmdClear with %d", configId));
+
+ try {
+ Utils.getReportList(configId, true /* clearData */, sLogger);
+ } catch (IOException | InterruptedException e) {
+ sLogger.severe("Failed to get report list: " + e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ // remove [CONFIG_ID]
+ private static boolean cmdRemove(String[] args) {
+ // Get CONFIG_ID
+ long configId;
+ try {
+ configId = getConfigId(false, args, 1);
+ } catch (NumberFormatException e) {
+ sLogger.severe("Invalid config id provided.");
+ printHelp();
+ return false;
+ }
+ sLogger.fine(String.format("cmdRemove with %d", configId));
+
+ try {
+ Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG,
+ Utils.SHELL_UID, String.valueOf(configId));
+ } catch (InterruptedException | IOException e) {
+ sLogger.severe("Failed to remove config: " + e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Searches through the array to see if it contains (precisely) the given value, starting
+ * at the given firstIdx.
+ */
+ private static boolean contains(String[] array, int firstIdx, String value) {
+ if (value == null) return false;
+ if (firstIdx < 0) return false;
+ for (int i = firstIdx; i < array.length; i++) {
+ if (value.equals(array[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the config id from args[idx], or returns DEFAULT_CONFIG_ID if args[idx] does not exist.
+ * If justUseDefault, overrides and just uses DEFAULT_CONFIG_ID instead.
+ */
+ private static long getConfigId(boolean justUseDefault, String[] args, int idx)
+ throws NumberFormatException {
+ if (justUseDefault || args.length <= idx || idx < 0) {
+ return DEFAULT_CONFIG_ID;
+ }
+ try {
+ return Long.valueOf(args[idx]);
+ } catch (NumberFormatException e) {
+ sLogger.severe("Bad config id provided: " + args[idx]);
+ throw e;
+ }
+ }
+}
diff --git a/cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
similarity index 68%
rename from cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java
rename to cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index cc4e386..f7bd44a 100644
--- a/cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.statsd.testdrive;
+package com.android.statsd.shelltools.testdrive;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
@@ -21,20 +21,15 @@
import com.android.os.AtomsProto.Atom;
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.statsd.shelltools.Utils;
import com.google.common.io.Files;
import com.google.protobuf.TextFormat;
import com.google.protobuf.TextFormat.ParseException;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.logging.ConsoleHandler;
-import java.util.logging.Formatter;
import java.util.logging.Level;
-import java.util.logging.LogRecord;
import java.util.logging.Logger;
public class TestDrive {
@@ -42,10 +37,6 @@
public static final int PULL_ATOM_START = 10000;
public static final long ATOM_MATCHER_ID = 1234567;
- public static final String UPDATE_CONFIG_CMD = "cmd stats config update";
- public static final String DUMP_REPORT_CMD = "cmd stats dump-report";
- public static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
- public static final String CONFIG_UID = "2000"; // shell uid
public static final long CONFIG_ID = 54321;
private static boolean mIsPushedAtom = false;
@@ -53,6 +44,9 @@
private static final Logger logger = Logger.getLogger(TestDrive.class.getName());
public static void main(String[] args) {
+ TestDrive testDrive = new TestDrive();
+ Utils.setUpLogger(logger, false);
+
if (args.length != 1) {
logger.log(Level.SEVERE, "Usage: ./test_drive <atomId>");
return;
@@ -70,12 +64,6 @@
}
mIsPushedAtom = atomId < PULL_ATOM_START;
- TestDrive testDrive = new TestDrive();
- TestDriveFormatter formatter = new TestDriveFormatter();
- ConsoleHandler handler = new ConsoleHandler();
- handler.setFormatter(formatter);
- logger.addHandler(handler);
- logger.setUseParentHandlers(false);
try {
StatsdConfig config = testDrive.createConfig(atomId);
@@ -109,55 +97,21 @@
configFile.deleteOnExit();
Files.write(config.toByteArray(), configFile);
String remotePath = "/data/local/tmp/" + configFile.getName();
- runCommand(null, "adb", "push", configFile.getAbsolutePath(), remotePath);
- runCommand(
- null, "adb", "shell", "cat", remotePath, "|", UPDATE_CONFIG_CMD,
+ Utils.runCommand(null, logger, "adb", "push", configFile.getAbsolutePath(), remotePath);
+ Utils.runCommand(null, logger,
+ "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
String.valueOf(CONFIG_ID));
}
private void removeConfig() {
try {
- runCommand(null, "adb", "shell", REMOVE_CONFIG_CMD, String.valueOf(CONFIG_ID));
+ Utils.runCommand(null, logger,
+ "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
}
}
- // Runs a shell command. Output should go to outputFile. Returns error string.
- private String runCommand(File outputFile, String... commands)
- throws IOException, InterruptedException {
- // Run macro on target
- ProcessBuilder pb = new ProcessBuilder(commands);
- // pb.redirectErrorStream(true);
-
- if (outputFile != null && outputFile.exists() && outputFile.canWrite()) {
- pb.redirectOutput(outputFile);
- }
- Process process = pb.start();
-
- // capture any errors
- StringBuilder out = new StringBuilder();
- // Read output
- BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
- String line = null, previous = null;
- while ((line = br.readLine()) != null) {
- if (!line.equals(previous)) {
- previous = line;
- out.append(line).append('\n');
- logger.fine(line);
- }
- }
-
- // Check result
- if (process.waitFor() == 0) {
- logger.fine("Success!");
- } else {
- // Abnormal termination: Log command parameters and output and throw ExecutionException
- logger.log(Level.SEVERE, out.toString());
- }
- return out.toString();
- }
-
private StatsdConfig createConfig(int atomId) {
try {
if (mIsPushedAtom) {
@@ -210,37 +164,8 @@
return builder;
}
- private ConfigMetricsReportList getReportList() throws Exception {
- try {
- File outputFile = File.createTempFile("statsdret", ".bin");
- outputFile.deleteOnExit();
- runCommand(
- outputFile,
- "adb",
- "shell",
- DUMP_REPORT_CMD,
- String.valueOf(CONFIG_ID),
- "--include_current_bucket",
- "--proto");
- ConfigMetricsReportList reportList =
- ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile));
- return reportList;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- logger.log(
- Level.SEVERE,
- "Failed to fetch and parse the statsd output report. "
- + "Perhaps there is not a valid statsd config for the requested "
- + "uid="
- + CONFIG_UID
- + ", id="
- + CONFIG_ID
- + ".");
- throw (e);
- }
- }
-
private void dumpMetrics() throws Exception {
- ConfigMetricsReportList reportList = getReportList();
+ ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, logger);
// We may get multiple reports. Take the last one.
ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
// Really should be only one metric.
@@ -294,9 +219,4 @@
+ "\n"
+ "hash_strings_in_metric_report: false";
- public static class TestDriveFormatter extends Formatter {
- public String format(LogRecord record) {
- return record.getMessage() + "\n";
- }
- }
}
diff --git a/cmds/statsd/tools/localtools/testdrive_manifest.txt b/cmds/statsd/tools/localtools/testdrive_manifest.txt
new file mode 100644
index 0000000..625ebfa
--- /dev/null
+++ b/cmds/statsd/tools/localtools/testdrive_manifest.txt
@@ -0,0 +1 @@
+Main-class: com.android.statsd.shelltools.testdrive.TestDrive
diff --git a/cmds/statsd/tools/statsd-testdrive/Android.bp b/cmds/statsd/tools/statsd-testdrive/Android.bp
deleted file mode 100644
index f566bc7..0000000
--- a/cmds/statsd/tools/statsd-testdrive/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-java_binary_host {
- name: "statsd_testdrive",
- manifest: "manifest.txt",
- srcs: [
- "src/**/*.java",
- ],
- static_libs: [
- "platformprotos",
- "guava",
- ],
-}
diff --git a/cmds/statsd/tools/statsd-testdrive/manifest.txt b/cmds/statsd/tools/statsd-testdrive/manifest.txt
deleted file mode 100644
index 0266d11..0000000
--- a/cmds/statsd/tools/statsd-testdrive/manifest.txt
+++ /dev/null
@@ -1 +0,0 @@
-Main-class: com.android.statsd.testdrive.TestDrive
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index f3bf6e7..a39f5e3 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -46,6 +46,7 @@
private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account";
+ private static final String COMMAND_SET_TEST_CALL_REDIRECTION_APP = "set-test-call-redirection-app";
private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
"add-or-remove-call-companion-app";
@@ -68,6 +69,7 @@
"usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" +
"usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" +
"usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" +
+ "usage: telecom set-test-call-redirection-app <PACKAGE>\n" +
"usage: telecom set-test-call-screening-app <PACKAGE>\n" +
"usage: telecom set-test-auto-mode-app <PACKAGE>\n" +
"usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" +
@@ -120,6 +122,9 @@
case COMMAND_REGISTER_PHONE_ACCOUNT:
runRegisterPhoneAccount();
break;
+ case COMMAND_SET_TEST_CALL_REDIRECTION_APP:
+ runSetTestCallRedirectionApp();
+ break;
case COMMAND_SET_TEST_CALL_SCREENING_APP:
runSetTestCallScreeningApp();
break;
@@ -189,6 +194,11 @@
System.out.println("Success - " + handle + " registered.");
}
+ private void runSetTestCallRedirectionApp() throws RemoteException {
+ final String packageName = nextArg();
+ mTelecomService.setTestDefaultCallRedirectionApp(packageName);
+ }
+
private void runSetTestCallScreeningApp() throws RemoteException {
final String packageName = nextArg();
mTelecomService.setTestDefaultCallScreeningApp(packageName);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index af3da0c..f928501 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -196,8 +196,26 @@
public abstract void updateOomAdj();
public abstract void updateCpuStats();
- public abstract void updateUsageStats(
+
+ /**
+ * Update battery stats on activity usage.
+ * @param activity
+ * @param uid
+ * @param userId
+ * @param started
+ */
+ public abstract void updateBatteryStats(
ComponentName activity, int uid, int userId, boolean resumed);
+
+ /**
+ * Update UsageStats of the activity.
+ * @param activity
+ * @param userId
+ * @param event
+ * @param appToken ActivityRecord's appToken.
+ */
+ public abstract void updateActivityUsageStats(
+ ComponentName activity, int userId, int event, IBinder appToken);
public abstract void updateForegroundTimeIfOnBattery(
String packageName, int uid, long cpuTimeDiff);
public abstract void sendForegroundProfileChanged(int userId);
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6fdf7c8..4d8c856 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -303,9 +303,7 @@
* @param stackId Id of stack to move the top activity to pinned stack.
* @param bounds Bounds to use for pinned stack.
* @return True if the top activity of stack was successfully moved to the pinned stack.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
try {
@@ -318,9 +316,7 @@
/**
* Start to enter lock task mode for given task by system(UI).
* @param taskId Id of task to lock.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void startSystemLockTaskMode(int taskId) {
try {
@@ -332,9 +328,7 @@
/**
* Stop lock task mode by system(UI).
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void stopSystemLockTaskMode() {
try {
@@ -349,9 +343,7 @@
* @param taskId Id of the task to move.
* @param stackId Id of the stack for task moving.
* @param toTop Whether the given task should shown to top of stack.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
try {
@@ -366,9 +358,7 @@
* @param stackId Id of the stack to resize.
* @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
* @param animate Whether we should play an animation for resizing stack.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void resizeStack(int stackId, Rect bounds, boolean animate) {
try {
@@ -383,9 +373,7 @@
* Resize task to given bounds.
* @param taskId Id of task to resize.
* @param bounds Bounds to resize task.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void resizeTask(int taskId, Rect bounds) {
try {
@@ -399,9 +387,7 @@
* Resize docked stack & its task to given stack & task bounds.
* @param stackBounds Bounds to resize stack.
* @param taskBounds Bounds to resize task.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void resizeDockedStack(Rect stackBounds, Rect taskBounds) {
try {
@@ -413,9 +399,7 @@
/**
* List all activity stacks information.
- * @hide
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public String listAllStacks() {
final List<ActivityManager.StackInfo> stacks;
@@ -438,7 +422,6 @@
* 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 {
diff --git a/core/java/android/app/RecoverableSecurityException.java b/core/java/android/app/RecoverableSecurityException.java
index 6747004..7cc3dab 100644
--- a/core/java/android/app/RecoverableSecurityException.java
+++ b/core/java/android/app/RecoverableSecurityException.java
@@ -16,15 +16,13 @@
package android.app;
-import android.content.ContentProvider;
-import android.content.ContentResolver;
+import android.annotation.NonNull;
import android.content.Context;
-import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
/**
* Specialization of {@link SecurityException} that contains additional
@@ -35,18 +33,11 @@
* authentication credentials, or granting access.
* <p>
* If the receiving app is actively involved with the user, it should present
- * the contained recovery details to help the user make forward progress. The
- * {@link #showAsDialog(Activity)} and
- * {@link #showAsNotification(Context, String)} methods are provided as a
- * convenience, but receiving apps are encouraged to use
- * {@link #getUserMessage()} and {@link #getUserAction()} to integrate in a more
- * natural way if relevant.
+ * the contained recovery details to help the user make forward progress.
* <p class="note">
* Note: legacy code that receives this exception may treat it as a general
* {@link SecurityException}, and thus there is no guarantee that the messages
* contained will be shown to the end user.
- *
- * @hide
*/
public final class RecoverableSecurityException extends SecurityException implements Parcelable {
private static final String TAG = "RecoverableSecurityException";
@@ -78,53 +69,28 @@
* apps that observe {@link Activity#RESULT_OK} may choose to
* immediately retry their operation.
*/
- public RecoverableSecurityException(Throwable cause, CharSequence userMessage,
- RemoteAction userAction) {
+ public RecoverableSecurityException(@NonNull Throwable cause, @NonNull CharSequence userMessage,
+ @NonNull RemoteAction userAction) {
super(cause.getMessage());
- mUserMessage = Preconditions.checkNotNull(userMessage);
- mUserAction = Preconditions.checkNotNull(userAction);
- }
-
- /** {@hide} */
- @Deprecated
- public RecoverableSecurityException(Throwable cause, CharSequence userMessage,
- CharSequence userActionTitle, PendingIntent userAction) {
- this(cause, userMessage,
- new RemoteAction(
- Icon.createWithResource("android",
- com.android.internal.R.drawable.ic_restart),
- userActionTitle, userActionTitle, userAction));
+ mUserMessage = Objects.requireNonNull(userMessage);
+ mUserAction = Objects.requireNonNull(userAction);
}
/**
* Return short message describing the issue for end user audiences, which
* may be shown in a notification or dialog.
*/
- public CharSequence getUserMessage() {
+ public @NonNull CharSequence getUserMessage() {
return mUserMessage;
}
/**
* Return primary action that will initiate the recovery.
*/
- public RemoteAction getUserAction() {
+ public @NonNull RemoteAction getUserAction() {
return mUserAction;
}
- /** @removed */
- @Deprecated
- public void showAsNotification(Context context) {
- final NotificationManager nm = context.getSystemService(NotificationManager.class);
-
- // Create a channel per-sender, since we don't want one poorly behaved
- // remote app to cause all of our notifications to be blocked
- final String channelId = TAG + "_" + mUserAction.getActionIntent().getCreatorUid();
- nm.createNotificationChannel(new NotificationChannel(channelId, TAG,
- NotificationManager.IMPORTANCE_DEFAULT));
-
- showAsNotification(context, channelId);
- }
-
/**
* Convenience method that will show a very simple notification populated
* with the details from this exception.
@@ -142,6 +108,7 @@
* @param channelId the {@link NotificationChannel} to use, which must have
* been already created using
* {@link NotificationManager#createNotificationChannel}.
+ * @hide
*/
public void showAsNotification(Context context, String channelId) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -167,6 +134,8 @@
* <p>
* This method will only display the most recent exception from any single
* remote UID; dialogs from older exceptions will always be replaced.
+ *
+ * @hide
*/
public void showAsDialog(Activity activity) {
final LocalDialog dialog = new LocalDialog();
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index 85fe99d..ebbf317 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -93,6 +93,22 @@
/** The user selected one of the choices from {@link #getChoices}. */
public static final int SOURCE_CHOICE = 1;
+ /** @hide */
+ @IntDef(prefix = {"EDIT_CHOICES_BEFORE_SENDING_"},
+ value = {EDIT_CHOICES_BEFORE_SENDING_AUTO, EDIT_CHOICES_BEFORE_SENDING_DISABLED,
+ EDIT_CHOICES_BEFORE_SENDING_ENABLED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EditChoicesBeforeSending {}
+
+ /** The platform will determine whether choices will be edited before being sent to the app. */
+ public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0;
+
+ /** Tapping on a choice should send the input immediately, without letting the user edit it. */
+ public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1;
+
+ /** Tapping on a choice should let the user edit the input before it is sent to the app. */
+ public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2;
+
// Flags bitwise-ored to mFlags
private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1;
@@ -103,17 +119,25 @@
private final CharSequence mLabel;
private final CharSequence[] mChoices;
private final int mFlags;
+ @EditChoicesBeforeSending private final int mEditChoicesBeforeSending;
private final Bundle mExtras;
private final ArraySet<String> mAllowedDataTypes;
private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
- int flags, Bundle extras, ArraySet<String> allowedDataTypes) {
+ int flags, int editChoicesBeforeSending, Bundle extras,
+ ArraySet<String> allowedDataTypes) {
this.mResultKey = resultKey;
this.mLabel = label;
this.mChoices = choices;
this.mFlags = flags;
+ this.mEditChoicesBeforeSending = editChoicesBeforeSending;
this.mExtras = extras;
this.mAllowedDataTypes = allowedDataTypes;
+ if (getEditChoicesBeforeSending() == EDIT_CHOICES_BEFORE_SENDING_ENABLED
+ && !getAllowFreeFormInput()) {
+ throw new IllegalArgumentException(
+ "setEditChoicesBeforeSending requires setAllowFreeFormInput");
+ }
}
/**
@@ -169,6 +193,15 @@
}
/**
+ * Gets whether tapping on a choice should let the user edit the input before it is sent to the
+ * app.
+ */
+ @EditChoicesBeforeSending
+ public int getEditChoicesBeforeSending() {
+ return mEditChoicesBeforeSending;
+ }
+
+ /**
* Get additional metadata carried around with this remote input.
*/
public Bundle getExtras() {
@@ -185,6 +218,8 @@
private CharSequence mLabel;
private CharSequence[] mChoices;
private int mFlags = DEFAULT_FLAGS;
+ @EditChoicesBeforeSending
+ private int mEditChoicesBeforeSending = EDIT_CHOICES_BEFORE_SENDING_AUTO;
/**
* Create a builder object for {@link RemoteInput} objects.
@@ -269,7 +304,20 @@
*/
@NonNull
public Builder setAllowFreeFormInput(boolean allowFreeFormTextInput) {
- setFlag(mFlags, allowFreeFormTextInput);
+ setFlag(FLAG_ALLOW_FREE_FORM_INPUT, allowFreeFormTextInput);
+ return this;
+ }
+
+ /**
+ * Specifies whether tapping on a choice should let the user edit the input before it is
+ * sent to the app. The default is {@link #EDIT_CHOICES_BEFORE_SENDING_AUTO}.
+ *
+ * It cannot be used if {@link #setAllowFreeFormInput} has been set to false.
+ */
+ @NonNull
+ public Builder setEditChoicesBeforeSending(
+ @EditChoicesBeforeSending int editChoicesBeforeSending) {
+ mEditChoicesBeforeSending = editChoicesBeforeSending;
return this;
}
@@ -312,8 +360,8 @@
*/
@NonNull
public RemoteInput build() {
- return new RemoteInput(
- mResultKey, mLabel, mChoices, mFlags, mExtras, mAllowedDataTypes);
+ return new RemoteInput(mResultKey, mLabel, mChoices, mFlags, mEditChoicesBeforeSending,
+ mExtras, mAllowedDataTypes);
}
}
@@ -322,6 +370,7 @@
mLabel = in.readCharSequence();
mChoices = in.readCharSequenceArray();
mFlags = in.readInt();
+ mEditChoicesBeforeSending = in.readInt();
mExtras = in.readBundle();
mAllowedDataTypes = (ArraySet<String>) in.readArraySet(null);
}
@@ -507,6 +556,7 @@
out.writeCharSequence(mLabel);
out.writeCharSequenceArray(mChoices);
out.writeInt(mFlags);
+ out.writeInt(mEditChoicesBeforeSending);
out.writeBundle(mExtras);
out.writeArraySet(mAllowedDataTypes);
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 257122d..2990b57 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -673,6 +673,15 @@
}
/**
+ * Returns true if the windowingMode represents a split window.
+ * @hide
+ */
+ public static boolean isSplitScreenWindowingMode(int windowingMode) {
+ return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ /**
* Returns true if the windows associated with this window configuration can receive input keys.
* @hide
*/
diff --git a/core/java/android/app/admin/DelegatedAdminReceiver.java b/core/java/android/app/admin/DelegatedAdminReceiver.java
new file mode 100644
index 0000000..0da4e7e
--- /dev/null
+++ b/core/java/android/app/admin/DelegatedAdminReceiver.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.app.admin;
+
+import static android.app.admin.DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS;
+import static android.app.admin.DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_URI;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_NETWORK_LOGS_COUNT;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.security.KeyChain;
+import android.util.Log;
+
+/**
+ * Base class for delegated apps to handle callbacks related to their delegated capabilities.
+ *
+ * <p>Delegated apps are apps that receive additional capabilities from the profile owner or
+ * device owner apps. Some of these capabilities involve the framework calling into the apps.
+ * To receive these callbacks, delegated apps should subclass this class and override the
+ * appropriate methods here. The subclassed receiver needs to be published in the app's
+ * manifest, with appropriate intent filters to mark which callbacks the receiver is interested
+ * in. An app can have multiple receivers as long as they listen for disjoint set of callbacks.
+ * For the manifest definitions, it must be protected by the
+ * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission to ensure only
+ * the system can trigger these callbacks.
+ *
+ * <p>The callback methods happen on the main thread of the process. Thus long running
+ * operations must be done on another thread. Note that because a receiver
+ * is done once returning from its onReceive function, such long-running operations
+ * should probably be done in a {@link Service}.
+ *
+ * @see DevicePolicyManager#setDelegatedScopes
+ * @see DeviceAdminReceiver
+ */
+public class DelegatedAdminReceiver extends BroadcastReceiver {
+ private static final String TAG = "DelegatedAdminReceiver";
+
+ /**
+ * Allows this receiver to select the alias for a private key and certificate pair for
+ * authentication. If this method returns null, the default {@link android.app.Activity} will
+ * be shown that lets the user pick a private key and certificate pair.
+ *
+ * <p> This callback is only applicable if the delegated app has
+ * {@link DevicePolicyManager#DELEGATION_CERT_SELECTION} capability. Additionally, it must
+ * declare an intent fitler for {@link #ACTION_CHOOSE_PRIVATE_KEY_ALIAS} in the receiver's
+ * manifest in order to receive this callback.
+ *
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param uid The uid asking for the private key and certificate pair.
+ * @param uri The URI to authenticate, may be null.
+ * @param alias The alias preselected by the client, or null.
+ * @return The private key alias to return and grant access to.
+ * @see KeyChain#choosePrivateKeyAlias
+ */
+ public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, Uri uri,
+ String alias) {
+ return null;
+ }
+
+ /**
+ * Called each time a new batch of network logs can be retrieved. This callback method will only
+ * ever be called when network logging is enabled. The logs can only be retrieved while network
+ * logging is enabled.
+ *
+ * <p>If a secondary user or profile is created, this callback won't be received until all users
+ * become affiliated again (even if network logging is enabled). It will also no longer be
+ * possible to retrieve the network logs batch with the most recent {@code batchToken} provided
+ * by this callback. See {@link DevicePolicyManager#setAffiliationIds}.
+ *
+ * <p> This callback is only applicable if the delegated app has
+ * {@link DevicePolicyManager#DELEGATION_NETWORK_LOGGING} capability. Additionally, it must
+ * declare an intent fitler for {@link #ACTION_NETWORK_LOGS_AVAILABLE} in the receiver's
+ * manifest in order to receive this callback.
+ *
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param batchToken The token representing the current batch of network logs.
+ * @param networkLogsCount The total count of events in the current batch of network logs.
+ * @see DevicePolicyManager#retrieveNetworkLogs
+ */
+ public void onNetworkLogsAvailable(Context context, Intent intent, long batchToken,
+ int networkLogsCount) {
+ }
+
+ /**
+ * Intercept delegated device administrator broadcasts. Implementations should not override
+ * this method; implement the convenience callbacks for each action instead.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (ACTION_CHOOSE_PRIVATE_KEY_ALIAS.equals(action)) {
+ int uid = intent.getIntExtra(EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID, -1);
+ Uri uri = intent.getParcelableExtra(EXTRA_CHOOSE_PRIVATE_KEY_URI);
+ String alias = intent.getStringExtra(EXTRA_CHOOSE_PRIVATE_KEY_ALIAS);
+ String chosenAlias = onChoosePrivateKeyAlias(context, intent, uid, uri, alias);
+ setResultData(chosenAlias);
+ } else if (ACTION_NETWORK_LOGS_AVAILABLE.equals(action)) {
+ long batchToken = intent.getLongExtra(EXTRA_NETWORK_LOGS_TOKEN, -1);
+ int networkLogsCount = intent.getIntExtra(EXTRA_NETWORK_LOGS_COUNT, 0);
+ onNetworkLogsAvailable(context, intent, batchToken, networkLogsCount);
+ } else {
+ Log.w(TAG, "Unhandled broadcast: " + action);
+ }
+ }
+}
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 1c9477d..5a7124e 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -23,7 +23,6 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -297,7 +296,7 @@
/**
* Broadcast action: notify that a new batch of network logs is ready to be collected.
* @see DeviceAdminReceiver#onNetworkLogsAvailable
- * @hide
+ * @see DelegatedAdminReceiver#onNetworkLogsAvailable
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
@@ -426,7 +425,11 @@
*/
public static final int BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE = 1;
- /** @hide */
+ /**
+ * Broadcast action: notify that some app is attempting to choose a KeyChain key.
+ * @see DeviceAdminReceiver#onChoosePrivateKeyAlias
+ * @see DelegatedAdminReceiver#onChoosePrivateKeyAlias
+ */
public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS =
"android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";
@@ -755,7 +758,6 @@
* @deprecated Do not use
*/
@Deprecated
- @SystemApi
public void onReadyForUserInitialization(Context context, Intent intent) {
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 81eac5a..670f8db 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,6 +16,7 @@
package android.app.admin;
+import android.Manifest.permission;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
import android.annotation.IntDef;
@@ -66,6 +67,7 @@
import android.os.UserManager;
import android.os.UserManager.UserOperationException;
import android.os.UserManager.UserOperationResult;
+import android.provider.CalendarContract;
import android.provider.ContactsContract.Directory;
import android.provider.Settings;
import android.security.AttestedKeyPair;
@@ -1381,6 +1383,73 @@
= "android.app.action.SET_NEW_PASSWORD";
/**
+ * Constant for {@link #getPasswordComplexity()}: no password.
+ *
+ * <p>Note that these complexity constants are ordered so that higher values are more complex.
+ */
+ public static final int PASSWORD_COMPLEXITY_NONE = 0;
+
+ /**
+ * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following:
+ * <ul>
+ * <li>pattern
+ * <li>PIN with repeating (4444) or ordered (1234, 4321, 2468) sequences
+ * </ul>
+ *
+ * <p>Note that these complexity constants are ordered so that higher values are more complex.
+ *
+ * @see #PASSWORD_QUALITY_SOMETHING
+ * @see #PASSWORD_QUALITY_NUMERIC
+ */
+ public static final int PASSWORD_COMPLEXITY_LOW = 0x10000;
+
+ /**
+ * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following:
+ * <ul>
+ * <li>PIN with <b>no</b> repeating (4444) or ordered (1234, 4321, 2468) sequences, length at
+ * least 4
+ * <li>alphabetic, length at least 4
+ * <li>alphanumeric, length at least 4
+ * </ul>
+ *
+ * <p>Note that these complexity constants are ordered so that higher values are more complex.
+ *
+ * @see #PASSWORD_QUALITY_NUMERIC_COMPLEX
+ * @see #PASSWORD_QUALITY_ALPHABETIC
+ * @see #PASSWORD_QUALITY_ALPHANUMERIC
+ */
+ public static final int PASSWORD_COMPLEXITY_MEDIUM = 0x30000;
+
+ /**
+ * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following:
+ * <ul>
+ * <li>PIN with <b>no</b> repeating (4444) or ordered (1234, 4321, 2468) sequences, length at
+ * least 4
+ * <li>alphabetic, length at least 6
+ * <li>alphanumeric, length at least 6
+ * </ul>
+ *
+ * <p>Note that these complexity constants are ordered so that higher values are more complex.
+ *
+ * @see #PASSWORD_QUALITY_NUMERIC_COMPLEX
+ * @see #PASSWORD_QUALITY_ALPHABETIC
+ * @see #PASSWORD_QUALITY_ALPHANUMERIC
+ */
+ public static final int PASSWORD_COMPLEXITY_HIGH = 0x50000;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"PASSWORD_COMPLEXITY_"}, value = {
+ PASSWORD_COMPLEXITY_NONE,
+ PASSWORD_COMPLEXITY_LOW,
+ PASSWORD_COMPLEXITY_MEDIUM,
+ PASSWORD_COMPLEXITY_HIGH,
+ })
+ public @interface PasswordComplexity {}
+
+ /**
* Activity action: have the user enter a new password for the parent profile.
* If the intent is launched from within a managed profile, this will trigger
* entering a new password for the parent of the profile. In all other cases
@@ -1546,12 +1615,46 @@
/**
* Delegation of management of uninstalled packages. This scope grants access to the
- * {@code #setKeepUninstalledPackages} and {@code #getKeepUninstalledPackages} APIs.
+ * {@link #setKeepUninstalledPackages} and {@link #getKeepUninstalledPackages} APIs.
*/
public static final String DELEGATION_KEEP_UNINSTALLED_PACKAGES =
"delegation-keep-uninstalled-packages";
/**
+ * Grants access to {@link #setNetworkLoggingEnabled}, {@link #isNetworkLoggingEnabled} and
+ * {@link #retrieveNetworkLogs}. Once granted the delegated app will start receiving
+ * DelegatedAdminReceiver.onNetworkLogsAvailable() callback, and Device owner will no longer
+ * receive the DeviceAdminReceiver.onNetworkLogsAvailable() callback.
+ * There can be at most one app that has this delegation.
+ * If another app already had delegated network logging access,
+ * it will lose the delegation when a new app is delegated.
+ *
+ * <p> Can only be granted by Device Owner.
+ */
+ public static final String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";
+
+ /**
+ * Grants access to selection of KeyChain certificates on behalf of requesting apps.
+ * Once granted the app will start receiving
+ * DelegatedAdminReceiver.onChoosePrivateKeyAlias. The caller (PO/DO) will
+ * no longer receive {@link DeviceAdminReceiver#onChoosePrivateKeyAlias()}.
+ * There can be at most one app that has this delegation.
+ * If another app already had delegated certificate selection access,
+ * it will lose the delegation when a new app is delegated.
+ *
+ * <p> Can be granted by Device Owner or Profile Owner.
+ */
+ public static final String DELEGATION_CERT_SELECTION = "delegation-cert-selection";
+
+
+ /**
+ * Delegation of silent APK installation via {@link android.content.pm.PackageInstaller} APIs.
+ *
+ * <p> Can only be delegated by Device Owner.
+ */
+ public static final String DELEGATION_PACKAGE_INSTALLATION = "delegation-package-installation";
+
+ /**
* No management for current user in-effect. This is the default.
* @hide
*/
@@ -3071,6 +3174,33 @@
}
/**
+ * Returns how complex the current user's screen lock is.
+ *
+ * <p>Note that when called from a profile which uses an unified challenge with its parent, the
+ * screen lock complexity of the parent will be returned. However, this API does not support
+ * explicitly querying the parent profile screen lock complexity via {@link
+ * #getParentProfileInstance}.
+ *
+ * @throws IllegalStateException if the user is not unlocked.
+ * @throws SecurityException if the calling application does not have the permission
+ * {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY}
+ */
+ @PasswordComplexity
+ @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY)
+ public int getPasswordComplexity() {
+ throwIfParentInstance("getPasswordComplexity");
+ if (mService == null) {
+ return PASSWORD_COMPLEXITY_NONE;
+ }
+
+ try {
+ return mService.getPasswordComplexity();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* When called by a profile owner of a managed profile returns true if the profile uses unified
* challenge with its parent user.
*
@@ -9238,7 +9368,8 @@
}
/**
- * Called by a device owner to control the network logging feature.
+ * Called by a device owner or delegated app with {@link #DELEGATION_NETWORK_LOGGING} to
+ * control the network logging feature.
*
* <p> Network logs contain DNS lookup and connect() library call events. The following library
* functions are recorded while network logging is active:
@@ -9275,16 +9406,17 @@
* all users to become affiliated. Therefore it's recommended that affiliation ids are set for
* new users as soon as possible after provisioning via {@link #setAffiliationIds}.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if called by a delegated app.
* @param enabled whether network logging should be enabled or not.
* @throws SecurityException if {@code admin} is not a device owner.
* @see #setAffiliationIds
* @see #retrieveNetworkLogs
*/
- public void setNetworkLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
+ public void setNetworkLoggingEnabled(@Nullable ComponentName admin, boolean enabled) {
throwIfParentInstance("setNetworkLoggingEnabled");
try {
- mService.setNetworkLoggingEnabled(admin, enabled);
+ mService.setNetworkLoggingEnabled(admin, mContext.getPackageName(), enabled);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -9294,7 +9426,8 @@
* Return whether network logging is enabled by a device owner.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with. Can only
- * be {@code null} if the caller has MANAGE_USERS permission.
+ * be {@code null} if the caller is a delegated app with {@link #DELEGATION_NETWORK_LOGGING}
+ * or has MANAGE_USERS permission.
* @return {@code true} if network logging is enabled by device owner, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not a device owner and caller has
* no MANAGE_USERS permission
@@ -9302,14 +9435,15 @@
public boolean isNetworkLoggingEnabled(@Nullable ComponentName admin) {
throwIfParentInstance("isNetworkLoggingEnabled");
try {
- return mService.isNetworkLoggingEnabled(admin);
+ return mService.isNetworkLoggingEnabled(admin, mContext.getPackageName());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
/**
- * Called by device owner to retrieve the most recent batch of network logging events.
+ * Called by device owner or delegated app with {@link #DELEGATION_NETWORK_LOGGING} to retrieve
+ * the most recent batch of network logging events.
* A device owner has to provide a batchToken provided as part of
* {@link DeviceAdminReceiver#onNetworkLogsAvailable} callback. If the token doesn't match the
* token of the most recent available batch of logs, {@code null} will be returned.
@@ -9328,7 +9462,8 @@
* by {@link DeviceAdminReceiver#onNetworkLogsAvailable}. See
* {@link DevicePolicyManager#setAffiliationIds}.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if called by a delegated app.
* @param batchToken A token of the batch to retrieve
* @return A new batch of network logs which is a list of {@link NetworkEvent}. Returns
* {@code null} if the batch represented by batchToken is no longer available or if
@@ -9338,11 +9473,11 @@
* @see #setAffiliationIds
* @see DeviceAdminReceiver#onNetworkLogsAvailable
*/
- public @Nullable List<NetworkEvent> retrieveNetworkLogs(@NonNull ComponentName admin,
+ public @Nullable List<NetworkEvent> retrieveNetworkLogs(@Nullable ComponentName admin,
long batchToken) {
throwIfParentInstance("retrieveNetworkLogs");
try {
- return mService.retrieveNetworkLogs(admin, batchToken);
+ return mService.retrieveNetworkLogs(admin, mContext.getPackageName(), batchToken);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -10308,4 +10443,33 @@
}
return false;
}
+
+ /**
+ * Starts an activity to view calendar events in the managed profile.
+ *
+ * @param eventId the id of the event to be viewed.
+ * @param start the start time of the event.
+ * @param end the end time of the event.
+ * @param allDay if the event is an all-day event.
+ * @param flags flags to be set for the intent
+ * @return {@code true} if the activity is started successfully. {@code false} otherwise.
+ *
+ * @see CalendarContract#startViewCalendarEventInManagedProfile(Context, String, long, long,
+ * long, boolean, int)
+ *
+ * @hide
+ */
+ public boolean startViewCalendarEventInManagedProfile(long eventId, long start, long end,
+ boolean allDay, int flags) {
+ throwIfParentInstance("startViewCalendarEventInManagedProfile");
+ if (mService != null) {
+ try {
+ return mService.startViewCalendarEventInManagedProfile(mContext.getPackageName(),
+ eventId, start, end, allDay, flags);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index de92978..8765760 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -118,6 +118,12 @@
public abstract boolean isUserAffiliatedWithDevice(int userId);
/**
+ * Returns whether the calling package can install or uninstall packages without user
+ * interaction.
+ */
+ public abstract boolean canSilentlyInstallPackage(String callerPackage, int callerUid);
+
+ /**
* Reports that a profile has changed to use a unified or separate credential.
*
* @param userId User ID of the profile.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 1ff4146..568becf 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -82,6 +82,7 @@
boolean isActivePasswordSufficient(int userHandle, boolean parent);
boolean isProfileActivePasswordSufficientForParent(int userHandle);
+ int getPasswordComplexity();
boolean isUsingUnifiedPassword(in ComponentName admin);
int getCurrentFailedPasswordAttempts(int userHandle, boolean parent);
int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent);
@@ -366,9 +367,9 @@
void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
boolean isBackupServiceEnabled(in ComponentName admin);
- void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled);
- boolean isNetworkLoggingEnabled(in ComponentName admin);
- List<NetworkEvent> retrieveNetworkLogs(in ComponentName admin, long batchToken);
+ void setNetworkLoggingEnabled(in ComponentName admin, in String packageName, boolean enabled);
+ boolean isNetworkLoggingEnabled(in ComponentName admin, in String packageName);
+ List<NetworkEvent> retrieveNetworkLogs(in ComponentName admin, in String packageName, long batchToken);
boolean bindDeviceAdminServiceAsUser(in ComponentName admin,
IApplicationThread caller, IBinder token, in Intent service,
@@ -431,4 +432,6 @@
boolean isManagedKiosk();
boolean isUnattendedManagedKiosk();
+
+ boolean startViewCalendarEventInManagedProfile(String packageName, long eventId, long start, long end, boolean allDay, int flags);
}
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 5fee853..8b41755 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -16,8 +16,14 @@
package android.app.admin;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,6 +41,8 @@
// consider it a complex PIN/password.
public static final int MAX_ALLOWED_SEQUENCE = 3;
+ // TODO(b/120536847): refactor isActivePasswordSufficient logic so that the actual password
+ // quality is not overwritten
public int quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
public int length = 0;
public int letters = 0;
@@ -46,6 +54,10 @@
public PasswordMetrics() {}
+ public PasswordMetrics(int quality) {
+ this.quality = quality;
+ }
+
public PasswordMetrics(int quality, int length) {
this.quality = quality;
this.length = length;
@@ -173,6 +185,15 @@
&& this.nonLetter == o.nonLetter;
}
+ private boolean satisfiesBucket(PasswordMetrics... bucket) {
+ for (PasswordMetrics metrics : bucket) {
+ if (this.quality == metrics.quality) {
+ return this.length >= metrics.length;
+ }
+ }
+ return false;
+ }
+
/*
* Returns the maximum length of a sequential characters. A sequence is defined as
* monotonically increasing characters with a constant interval or the same character repeated.
@@ -254,4 +275,99 @@
return 0;
}
}
+
+ /** Determines the {@link PasswordComplexity} of this {@link PasswordMetrics}. */
+ @PasswordComplexity
+ public int determineComplexity() {
+ for (PasswordComplexityBucket bucket : PasswordComplexityBucket.BUCKETS) {
+ if (satisfiesBucket(bucket.getMetrics())) {
+ return bucket.mComplexityLevel;
+ }
+ }
+ return PASSWORD_COMPLEXITY_NONE;
+ }
+
+ /**
+ * Requirements in terms of {@link PasswordMetrics} for each {@link PasswordComplexity}.
+ */
+ public static class PasswordComplexityBucket {
+ /**
+ * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_HIGH} in terms of
+ * {@link PasswordMetrics}.
+ */
+ private static final PasswordComplexityBucket HIGH =
+ new PasswordComplexityBucket(
+ PASSWORD_COMPLEXITY_HIGH,
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 6),
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 6),
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
+ 8));
+
+ /**
+ * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_MEDIUM} in terms of
+ * {@link PasswordMetrics}.
+ */
+ private static final PasswordComplexityBucket MEDIUM =
+ new PasswordComplexityBucket(
+ PASSWORD_COMPLEXITY_MEDIUM,
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 4),
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 4),
+ new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
+ 4));
+
+ /**
+ * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_LOW} in terms of
+ * {@link PasswordMetrics}.
+ */
+ private static final PasswordComplexityBucket LOW =
+ new PasswordComplexityBucket(
+ PASSWORD_COMPLEXITY_LOW,
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING));
+
+ /**
+ * A special bucket to represent {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}.
+ */
+ private static final PasswordComplexityBucket NONE =
+ new PasswordComplexityBucket(PASSWORD_COMPLEXITY_NONE, new PasswordMetrics());
+
+ /** Array containing all buckets from high to low. */
+ private static final PasswordComplexityBucket[] BUCKETS =
+ new PasswordComplexityBucket[] {HIGH, MEDIUM, LOW};
+
+ @PasswordComplexity
+ private final int mComplexityLevel;
+ private final PasswordMetrics[] mMetrics;
+
+ private PasswordComplexityBucket(@PasswordComplexity int complexityLevel,
+ PasswordMetrics... metrics) {
+ this.mComplexityLevel = complexityLevel;
+ this.mMetrics = metrics;
+ }
+
+ /** Returns the {@link PasswordMetrics} that meet the min requirements of this bucket. */
+ public PasswordMetrics[] getMetrics() {
+ return mMetrics;
+ }
+
+ /** Returns the bucket that {@code complexityLevel} represents. */
+ public static PasswordComplexityBucket complexityLevelToBucket(
+ @PasswordComplexity int complexityLevel) {
+ for (PasswordComplexityBucket bucket : BUCKETS) {
+ if (bucket.mComplexityLevel == complexityLevel) {
+ return bucket;
+ }
+ }
+ return NONE;
+ }
+ }
}
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index bcd5f6c..dd72845 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -689,13 +689,11 @@
mFreezePeriods.stream().map(n -> n.toString()).collect(Collectors.joining(",")));
}
- @SystemApi
@Override
public int describeContents() {
return 0;
}
- @SystemApi
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mPolicyType);
@@ -712,7 +710,6 @@
}
}
- @SystemApi
public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR =
new Parcelable.Creator<SystemUpdatePolicy>() {
diff --git a/core/java/android/app/role/IOnRoleHoldersChangedListener.aidl b/core/java/android/app/role/IOnRoleHoldersChangedListener.aidl
new file mode 100644
index 0000000..6cf961f
--- /dev/null
+++ b/core/java/android/app/role/IOnRoleHoldersChangedListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.role;
+
+/**
+ * @hide
+ */
+oneway interface IOnRoleHoldersChangedListener {
+
+ void onRoleHoldersChanged(String roleName, int userId);
+}
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 3ca8ec0..4ce0f31 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -16,6 +16,7 @@
package android.app.role;
+import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManagerCallback;
/**
@@ -37,6 +38,11 @@
void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback);
+ void addOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, int userId);
+
+ void removeOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener,
+ int userId);
+
void setRoleNamesFromController(in List<String> roleNames);
boolean addRoleHolderFromController(in String roleName, in String packageName);
diff --git a/core/java/android/app/role/OnRoleHoldersChangedListener.java b/core/java/android/app/role/OnRoleHoldersChangedListener.java
new file mode 100644
index 0000000..5958deb
--- /dev/null
+++ b/core/java/android/app/role/OnRoleHoldersChangedListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.role;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.UserHandle;
+
+/**
+ * Listener for role holder changes.
+ *
+ * @hide
+ */
+@SystemApi
+public interface OnRoleHoldersChangedListener {
+
+ /**
+ * Called when the holders of roles are changed.
+ *
+ * @param roleName the name of the role whose holders are changed
+ * @param user the user for this role holder change
+ */
+ void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user);
+}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index f3b2153..5d101ab 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -23,6 +23,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
@@ -30,8 +31,12 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.util.List;
import java.util.concurrent.Executor;
@@ -125,6 +130,13 @@
@NonNull
private final IRoleManager mService;
+ @GuardedBy("mListenersLock")
+ @NonNull
+ private final SparseArray<ArrayMap<OnRoleHoldersChangedListener,
+ OnRoleHoldersChangedListenerDelegate>> mListeners = new SparseArray<>();
+ @NonNull
+ private final Object mListenersLock = new Object();
+
/**
* @hide
*/
@@ -146,8 +158,6 @@
* @param roleName the name of requested role
*
* @return the {@code Intent} to prompt user to grant the role
- *
- * @throws IllegalArgumentException if {@code role} is {@code null} or empty
*/
@NonNull
public Intent createRequestRoleIntent(@NonNull String roleName) {
@@ -164,8 +174,6 @@
* @param roleName the name of role to checking for
*
* @return whether the role is available in the system
- *
- * @throws IllegalArgumentException if the role name is {@code null} or empty
*/
public boolean isRoleAvailable(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -182,8 +190,6 @@
* @param roleName the name of the role to check for
*
* @return whether the calling application is holding the role
- *
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
*/
public boolean isRoleHeld(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -204,8 +210,6 @@
*
* @return a list of package names of the role holders, or an empty list if none.
*
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
*
* @hide
@@ -230,8 +234,6 @@
*
* @return a list of package names of the role holders, or an empty list if none.
*
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
- *
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -266,8 +268,6 @@
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -306,8 +306,6 @@
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -345,8 +343,6 @@
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
@@ -371,6 +367,96 @@
}
/**
+ * Add a listener to observe role holder changes
+ * <p>
+ * <strong>Note:</strong> Using this API requires holding
+ * {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
+ * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
+ *
+ * @param executor the {@code Executor} to call the listener on.
+ * @param listener the listener to be added
+ * @param user the user to add the listener for
+ *
+ * @see #removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener, UserHandle)
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
+ @SystemApi
+ public void addOnRoleHoldersChangedListenerAsUser(@CallbackExecutor @NonNull Executor executor,
+ @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
+ Preconditions.checkNotNull(executor, "executor cannot be null");
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(user, "user cannot be null");
+ int userId = user.getIdentifier();
+ synchronized (mListenersLock) {
+ ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners =
+ mListeners.get(userId);
+ if (listeners == null) {
+ listeners = new ArrayMap<>();
+ mListeners.put(userId, listeners);
+ } else {
+ if (listeners.containsKey(listener)) {
+ return;
+ }
+ }
+ OnRoleHoldersChangedListenerDelegate listenerDelegate =
+ new OnRoleHoldersChangedListenerDelegate(executor, listener);
+ try {
+ mService.addOnRoleHoldersChangedListenerAsUser(listenerDelegate, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ listeners.put(listener, listenerDelegate);
+ }
+ }
+
+ /**
+ * Remove a listener observing role holder changes
+ * <p>
+ * <strong>Note:</strong> Using this API requires holding
+ * {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
+ * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
+ *
+ * @param listener the listener to be removed
+ * @param user the user to remove the listener for
+ *
+ * @see #addOnRoleHoldersChangedListenerAsUser(Executor, OnRoleHoldersChangedListener,
+ * UserHandle)
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
+ @SystemApi
+ public void removeOnRoleHoldersChangedListenerAsUser(
+ @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(user, "user cannot be null");
+ int userId = user.getIdentifier();
+ synchronized (mListenersLock) {
+ ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners =
+ mListeners.get(userId);
+ if (listeners == null) {
+ return;
+ }
+ OnRoleHoldersChangedListenerDelegate listenerDelegate = listeners.get(listener);
+ if (listenerDelegate == null) {
+ return;
+ }
+ try {
+ mService.removeOnRoleHoldersChangedListenerAsUser(listenerDelegate,
+ user.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ listeners.remove(listener);
+ if (listeners.isEmpty()) {
+ mListeners.remove(userId);
+ }
+ }
+ }
+
+ /**
* Set the names of all the available roles. Should only be called from
* {@link android.rolecontrollerservice.RoleControllerService}.
* <p>
@@ -379,8 +465,6 @@
*
* @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)
@@ -408,8 +492,6 @@
* @return whether the operation was successful, and will also be {@code true} if a matching
* role holder is already found.
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHolders(String)
* @see #removeRoleHolderFromController(String, String)
*
@@ -442,8 +524,6 @@
* @return whether the operation was successful, and will also be {@code true} if no matching
* role holder was found to remove.
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHolders(String)
* @see #addRoleHolderFromController(String, String)
*
@@ -495,4 +575,31 @@
}
}
}
+
+ private static class OnRoleHoldersChangedListenerDelegate
+ extends IOnRoleHoldersChangedListener.Stub {
+
+ @NonNull
+ private final Executor mExecutor;
+ @NonNull
+ private final OnRoleHoldersChangedListener mListener;
+
+ OnRoleHoldersChangedListenerDelegate(@NonNull Executor executor,
+ @NonNull OnRoleHoldersChangedListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(PooledLambda.obtainRunnable(
+ OnRoleHoldersChangedListener::onRoleHoldersChanged, mListener, roleName,
+ UserHandle.of(userId)));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java
index aaae57e5..a79ad2f 100644
--- a/core/java/android/app/usage/EventList.java
+++ b/core/java/android/app/usage/EventList.java
@@ -103,4 +103,21 @@
}
return result;
}
+
+ /**
+ * Remove events of certain type on or after a timestamp.
+ * @param type The type of event to remove.
+ * @param timeStamp the timeStamp on or after which to remove the event.
+ */
+ public void removeOnOrAfter(int type, long timeStamp) {
+ for (int i = mEvents.size() - 1; i >= 0; i--) {
+ UsageEvents.Event event = mEvents.get(i);
+ if (event.mTimeStamp < timeStamp) {
+ break;
+ }
+ if (event.mEventType == type) {
+ mEvents.remove(i);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 3a5975a..a06213d 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -50,13 +50,27 @@
public static final int NONE = 0;
/**
+ * @deprecated by {@link #ACTIVITY_RESUMED}
+ */
+ @Deprecated
+ public static final int MOVE_TO_FOREGROUND = 1;
+
+ /**
* An event type denoting that an {@link android.app.Activity} moved to the foreground.
* This event has a package name and class name associated with it and can be retrieved
* using {@link #getPackageName()} and {@link #getClassName()}.
* If a package has multiple activities, this event is reported for each activity that moves
* to foreground.
+ * This event is corresponding to {@link android.app.Activity#onResume()} of the
+ * activity's lifecycle.
*/
- public static final int MOVE_TO_FOREGROUND = 1;
+ public static final int ACTIVITY_RESUMED = MOVE_TO_FOREGROUND;
+
+ /**
+ * @deprecated by {@link #ACTIVITY_PAUSED}
+ */
+ @Deprecated
+ public static final int MOVE_TO_BACKGROUND = 2;
/**
* An event type denoting that an {@link android.app.Activity} moved to the background.
@@ -64,19 +78,21 @@
* using {@link #getPackageName()} and {@link #getClassName()}.
* If a package has multiple activities, this event is reported for each activity that moves
* to background.
+ * This event is corresponding to {@link android.app.Activity#onPause()} of the activity's
+ * lifecycle.
*/
- public static final int MOVE_TO_BACKGROUND = 2;
+ public static final int ACTIVITY_PAUSED = MOVE_TO_BACKGROUND;
/**
* An event type denoting that a component was in the foreground when the stats
- * rolled-over. This is effectively treated as a {@link #MOVE_TO_BACKGROUND}.
+ * rolled-over. This is effectively treated as a {@link #ACTIVITY_PAUSED}.
* {@hide}
*/
public static final int END_OF_DAY = 3;
/**
* An event type denoting that a component was in the foreground the previous day.
- * This is effectively treated as a {@link #MOVE_TO_FOREGROUND}.
+ * This is effectively treated as a {@link #ACTIVITY_RESUMED}.
* {@hide}
*/
public static final int CONTINUE_PREVIOUS_DAY = 4;
@@ -207,10 +223,31 @@
public static final int ROLLOVER_FOREGROUND_SERVICE = 22;
/**
+ * An activity becomes invisible on the UI, corresponding to
+ * {@link android.app.Activity#onStop()} of the activity's lifecycle.
+ */
+ public static final int ACTIVITY_STOPPED = 23;
+
+ /**
+ * An activity object is destroyed, corresponding to
+ * {@link android.app.Activity#onDestroy()} of the activity's lifecycle.
+ * {@hide}
+ */
+ public static final int ACTIVITY_DESTROYED = 24;
+
+ /**
+ * The event type demoting that a flush of UsageStatsDatabase to file system. Before the
+ * flush all usage stats need to be updated to latest timestamp to make sure the most
+ * up to date stats are persisted.
+ * @hide
+ */
+ public static final int FLUSH_TO_DISK = 25;
+
+ /**
* Keep in sync with the greatest event type value.
* @hide
*/
- public static final int MAX_EVENT_TYPE = 22;
+ public static final int MAX_EVENT_TYPE = 25;
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
@@ -240,6 +277,12 @@
@UnsupportedAppUsage
public String mClass;
+
+ /**
+ * {@hide}
+ */
+ public int mInstanceId;
+
/**
* {@hide}
*/
@@ -311,9 +354,16 @@
}
/** @hide */
+ public Event(int type, long timeStamp) {
+ mEventType = type;
+ mTimeStamp = timeStamp;
+ }
+
+ /** @hide */
public Event(Event orig) {
mPackage = orig.mPackage;
mClass = orig.mClass;
+ mInstanceId = orig.mInstanceId;
mTimeStamp = orig.mTimeStamp;
mEventType = orig.mEventType;
mConfiguration = orig.mConfiguration;
@@ -342,6 +392,16 @@
}
/**
+ * An activity can be instantiated multiple times, this is the unique activity instance ID.
+ * For non-activity class, instance ID is always zero.
+ * @hide
+ */
+ @SystemApi
+ public int getInstanceId() {
+ return mInstanceId;
+ }
+
+ /**
* The time at which this event occurred, measured in milliseconds since the epoch.
* <p/>
* See {@link System#currentTimeMillis()}.
@@ -352,12 +412,14 @@
/**
* The event type.
- *
- * @see #MOVE_TO_BACKGROUND
- * @see #MOVE_TO_FOREGROUND
+ * @see #ACTIVITY_PAUSED
+ * @see #ACTIVITY_RESUMED
* @see #CONFIGURATION_CHANGE
* @see #USER_INTERACTION
* @see #STANDBY_BUCKET_CHANGED
+ * @see #FOREGROUND_SERVICE_START
+ * @see #FOREGROUND_SERVICE_STOP
+ * @see #ACTIVITY_STOPPED
*/
public int getEventType() {
return mEventType;
@@ -576,6 +638,7 @@
}
p.writeInt(packageIndex);
p.writeInt(classIndex);
+ p.writeInt(event.mInstanceId);
p.writeInt(event.mEventType);
p.writeLong(event.mTimeStamp);
@@ -618,6 +681,7 @@
} else {
eventOut.mClass = null;
}
+ eventOut.mInstanceId = p.readInt();
eventOut.mEventType = p.readInt();
eventOut.mTimeStamp = p.readLong();
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 73426e4..8fb7f4c 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -16,13 +16,15 @@
package android.app.usage;
-import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.END_OF_DAY;
+import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
-import static android.app.usage.UsageEvents.Event.MOVE_TO_BACKGROUND;
-import static android.app.usage.UsageEvents.Event.MOVE_TO_FOREGROUND;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
import android.annotation.SystemApi;
@@ -31,6 +33,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import android.util.SparseIntArray;
/**
* Contains usage statistics for an app package for a specific
@@ -57,13 +60,20 @@
public long mEndTimeStamp;
/**
- * Last time used by the user with an explicit action (notification, activity launch)
+ * Last time an activity is at foreground (have focus), this is corresponding to
+ * {@link android.app.usage.UsageEvents.Event#ACTIVITY_RESUMED} event.
* {@hide}
*/
@UnsupportedAppUsage
public long mLastTimeUsed;
/**
+ * Last time an activity is visible.
+ * @hide
+ */
+ public long mLastTimeVisible;
+
+ /**
* Total time this package's activity is in foreground.
* {@hide}
*/
@@ -71,6 +81,12 @@
public long mTotalTimeInForeground;
/**
+ * Total time this package's activity is visible.
+ * {@hide}
+ */
+ public long mTotalTimeVisible;
+
+ /**
* Last time foreground service is started.
* {@hide}
*/
@@ -93,31 +109,32 @@
*/
public int mAppLaunchCount;
- /** Last activity MOVE_TO_FOREGROUND or MOVE_TO_BACKGROUND event.
+ /** Last activity ACTIVITY_RESUMED or ACTIVITY_PAUSED event.
* {@hide}
- * @deprecated use {@link #mLastForegroundActivityEventMap} instead.
+ * @deprecated use {@link #mActivities} instead.
*/
@UnsupportedAppUsage
@Deprecated
public int mLastEvent;
/**
- * If an activity is in foreground, it has one entry in this map.
- * When activity moves to background, it is removed from this map.
- * Key is activity class name.
- * Value is last time this activity MOVE_TO_FOREGROUND or MOVE_TO_BACKGROUND event.
+ * If an activity is visible(onStart(), onPause() states) or in foreground (onResume() state),
+ * it has one entry in this map. When an activity becomes invisible (onStop() or onDestroy()),
+ * it is removed from this map.
+ * Key is instanceId of the activity (ActivityRecode appToken hashCode)..
+ * Value is this activity's last event, one of ACTIVITY_RESUMED or
+ * ACTIVITY_PAUSED.
* {@hide}
*/
- public ArrayMap<String, Integer> mLastForegroundActivityEventMap = new ArrayMap<>();
-
+ public SparseIntArray mActivities = new SparseIntArray();
/**
* If a foreground service is started, it has one entry in this map.
- * When a foreground service is stopped, it is removed from this map.
+ * When a foreground service is stopped, it is removed from this set.
* Key is foreground service class name.
- * Value is last foreground service FOREGROUND_SERVICE_START ot FOREGROUND_SERVICE_STOP event.
+ * Value is the foreground service's last event, it is FOREGROUND_SERVICE_START.
* {@hide}
*/
- public ArrayMap<String, Integer> mLastForegroundServiceEventMap = new ArrayMap<>();
+ public ArrayMap<String, Integer> mForegroundServices = new ArrayMap<>();
/**
* {@hide}
@@ -135,14 +152,16 @@
mBeginTimeStamp = stats.mBeginTimeStamp;
mEndTimeStamp = stats.mEndTimeStamp;
mLastTimeUsed = stats.mLastTimeUsed;
+ mLastTimeVisible = stats.mLastTimeVisible;
mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed;
mTotalTimeInForeground = stats.mTotalTimeInForeground;
+ mTotalTimeVisible = stats.mTotalTimeVisible;
mTotalTimeForegroundServiceUsed = stats.mTotalTimeForegroundServiceUsed;
mLaunchCount = stats.mLaunchCount;
mAppLaunchCount = stats.mAppLaunchCount;
mLastEvent = stats.mLastEvent;
- mLastForegroundActivityEventMap = stats.mLastForegroundActivityEventMap;
- mLastForegroundServiceEventMap = stats.mLastForegroundServiceEventMap;
+ mActivities = stats.mActivities;
+ mForegroundServices = stats.mForegroundServices;
mChooserCounts = stats.mChooserCounts;
}
@@ -191,6 +210,14 @@
}
/**
+ * Get the last time this package's activity is visible in the UI, measured in milliseconds
+ * since the epoch.
+ */
+ public long getLastTimeVisible() {
+ return mLastTimeVisible;
+ }
+
+ /**
* Get the total time this package spent in the foreground, measured in milliseconds.
*/
public long getTotalTimeInForeground() {
@@ -198,6 +225,13 @@
}
/**
+ * Get the total time this package's activity is visible in the UI, measured in milliseconds.
+ */
+ public long getTotalTimeVisible() {
+ return mTotalTimeVisible;
+ }
+
+ /**
* Get the last time this package's foreground service was used, measured in milliseconds since
* the epoch.
* <p/>
@@ -224,6 +258,20 @@
return mAppLaunchCount;
}
+ private void mergeEventMap(SparseIntArray left, SparseIntArray right) {
+ final int size = right.size();
+ for (int i = 0; i < size; i++) {
+ final int instanceId = right.keyAt(i);
+ final int event = right.valueAt(i);
+ final int index = left.indexOfKey(instanceId);
+ if (index >= 0) {
+ left.put(instanceId, Math.max(left.valueAt(index), event));
+ } else {
+ left.put(instanceId, event);
+ }
+ }
+ }
+
private void mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right) {
final int size = right.size();
for (int i = 0; i < size; i++) {
@@ -255,15 +303,17 @@
if (right.mBeginTimeStamp > mBeginTimeStamp) {
// Even though incoming UsageStat begins after this one, its last time used fields
// may somehow be empty or chronologically preceding the older UsageStat.
- mergeEventMap(mLastForegroundActivityEventMap, right.mLastForegroundActivityEventMap);
- mergeEventMap(mLastForegroundServiceEventMap, right.mLastForegroundServiceEventMap);
+ mergeEventMap(mActivities, right.mActivities);
+ mergeEventMap(mForegroundServices, right.mForegroundServices);
mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed);
+ mLastTimeVisible = Math.max(mLastTimeVisible, right.mLastTimeVisible);
mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed,
right.mLastTimeForegroundServiceUsed);
}
mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
mTotalTimeInForeground += right.mTotalTimeInForeground;
+ mTotalTimeVisible += right.mTotalTimeVisible;
mTotalTimeForegroundServiceUsed += right.mTotalTimeForegroundServiceUsed;
mLaunchCount += right.mLaunchCount;
mAppLaunchCount += right.mAppLaunchCount;
@@ -290,36 +340,76 @@
}
/**
- * Tell if an event indicate activity is in foreground or not.
- * @param event the activity event.
- * @return true if activity is in foreground, false otherwise.
- * @hide
+ * Tell if any activity is in foreground.
+ * @return
*/
- private boolean isActivityInForeground(int event) {
- return event == MOVE_TO_FOREGROUND
- || event == CONTINUE_PREVIOUS_DAY;
+ private boolean hasForegroundActivity() {
+ final int size = mActivities.size();
+ for (int i = 0; i < size; i++) {
+ if (mActivities.valueAt(i) == ACTIVITY_RESUMED) {
+ return true;
+ }
+ }
+ return false;
}
/**
- * Tell if an event indicate foreground sevice is started or not.
- * @param event the foreground service event.
- * @return true if foreground service is started, false if stopped.
- * @hide
+ * Tell if any activity is visible.
+ * @return
*/
- private boolean isForegroundServiceStarted(int event) {
- return event == FOREGROUND_SERVICE_START
- || event == CONTINUING_FOREGROUND_SERVICE;
+ private boolean hasVisibleActivity() {
+ final int size = mActivities.size();
+ for (int i = 0; i < size; i++) {
+ final int type = mActivities.valueAt(i);
+ if (type == ACTIVITY_RESUMED
+ || type == ACTIVITY_PAUSED) {
+ return true;
+ }
+ }
+ return false;
}
/**
- * If any activity in foreground or any foreground service is started, the app is considered in
- * use.
- * @return true if in use, false otherwise.
- * @hide
+ * Tell if any foreground service is started.
+ * @return
*/
- private boolean isAppInUse() {
- return !mLastForegroundActivityEventMap.isEmpty()
- || !mLastForegroundServiceEventMap.isEmpty();
+ private boolean anyForegroundServiceStarted() {
+ return !mForegroundServices.isEmpty();
+ }
+
+ /**
+ * Increment total time in foreground and update last time in foreground.
+ * @param timeStamp current timestamp.
+ */
+ private void incrementTimeUsed(long timeStamp) {
+ if (timeStamp > mLastTimeUsed) {
+ mTotalTimeInForeground += timeStamp - mLastTimeUsed;
+ mLastTimeUsed = timeStamp;
+ }
+ }
+
+ /**
+ * Increment total time visible and update last time visible.
+ * @param timeStamp current timestmap.
+ */
+ private void incrementTimeVisible(long timeStamp) {
+ if (timeStamp > mLastTimeVisible) {
+ mTotalTimeVisible += timeStamp - mLastTimeVisible;
+ mLastTimeVisible = timeStamp;
+ }
+ }
+
+ /**
+ * Increment total time foreground service is used and update last time foreground service is
+ * used.
+ * @param timeStamp current timestamp.
+ */
+ private void incrementServiceTimeUsed(long timeStamp) {
+ if (timeStamp > mLastTimeForegroundServiceUsed) {
+ mTotalTimeForegroundServiceUsed +=
+ timeStamp - mLastTimeForegroundServiceUsed;
+ mLastTimeForegroundServiceUsed = timeStamp;
+ }
}
/**
@@ -327,33 +417,63 @@
* @param className className of the activity.
* @param timeStamp timeStamp of the event.
* @param eventType type of the event.
+ * @param instanceId hashCode of the ActivityRecord's appToken.
* @hide
*/
- private void updateForegroundActivity(String className, long timeStamp, int eventType) {
- if (eventType != MOVE_TO_BACKGROUND
- && eventType != MOVE_TO_FOREGROUND
- && eventType != END_OF_DAY) {
+ private void updateActivity(String className, long timeStamp, int eventType, int instanceId) {
+ if (eventType != ACTIVITY_RESUMED
+ && eventType != ACTIVITY_PAUSED
+ && eventType != ACTIVITY_STOPPED
+ && eventType != ACTIVITY_DESTROYED) {
return;
}
- final Integer lastEvent = mLastForegroundActivityEventMap.get(className);
- if (lastEvent != null) {
- if (isActivityInForeground(lastEvent)) {
- if (timeStamp > mLastTimeUsed) {
- mTotalTimeInForeground += timeStamp - mLastTimeUsed;
+ // update usage.
+ final int index = mActivities.indexOfKey(instanceId);
+ if (index >= 0) {
+ final int lastEvent = mActivities.valueAt(index);
+ switch (lastEvent) {
+ case ACTIVITY_RESUMED:
+ incrementTimeUsed(timeStamp);
+ incrementTimeVisible(timeStamp);
+ break;
+ case ACTIVITY_PAUSED:
+ incrementTimeVisible(timeStamp);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // update current event.
+ switch(eventType) {
+ case ACTIVITY_RESUMED:
+ if (!hasVisibleActivity()) {
+ // this is the first visible activity.
+ mLastTimeUsed = timeStamp;
+ mLastTimeVisible = timeStamp;
+ } else if (!hasForegroundActivity()) {
+ // this is the first foreground activity.
mLastTimeUsed = timeStamp;
}
- }
- if (eventType == MOVE_TO_BACKGROUND) {
- mLastForegroundActivityEventMap.remove(className);
- } else {
- mLastForegroundActivityEventMap.put(className, eventType);
- }
- } else if (eventType == MOVE_TO_FOREGROUND) {
- if (!isAppInUse()) {
- mLastTimeUsed = timeStamp;
- }
- mLastForegroundActivityEventMap.put(className, eventType);
+ mActivities.put(instanceId, eventType);
+ break;
+ case ACTIVITY_PAUSED:
+ if (!hasVisibleActivity()) {
+ // this is the first visible activity.
+ mLastTimeVisible = timeStamp;
+ }
+ mActivities.put(instanceId, eventType);
+ break;
+ case ACTIVITY_STOPPED:
+ mActivities.put(instanceId, eventType);
+ break;
+ case ACTIVITY_DESTROYED:
+ // remove activity from the map.
+ mActivities.delete(instanceId);
+ break;
+ default:
+ break;
}
}
@@ -366,80 +486,97 @@
*/
private void updateForegroundService(String className, long timeStamp, int eventType) {
if (eventType != FOREGROUND_SERVICE_STOP
- && eventType != FOREGROUND_SERVICE_START
- && eventType != ROLLOVER_FOREGROUND_SERVICE) {
+ && eventType != FOREGROUND_SERVICE_START) {
return;
}
- final Integer lastEvent = mLastForegroundServiceEventMap.get(className);
+ final Integer lastEvent = mForegroundServices.get(className);
+ // update usage.
if (lastEvent != null) {
- if (isForegroundServiceStarted(lastEvent)) {
- if (timeStamp > mLastTimeForegroundServiceUsed) {
- mTotalTimeForegroundServiceUsed +=
- timeStamp - mLastTimeForegroundServiceUsed;
+ switch (lastEvent) {
+ case FOREGROUND_SERVICE_START:
+ case CONTINUING_FOREGROUND_SERVICE:
+ incrementServiceTimeUsed(timeStamp);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // update current event.
+ switch (eventType) {
+ case FOREGROUND_SERVICE_START:
+ if (!anyForegroundServiceStarted()) {
mLastTimeForegroundServiceUsed = timeStamp;
}
- }
- if (eventType == FOREGROUND_SERVICE_STOP) {
- mLastForegroundServiceEventMap.remove(className);
- } else {
- mLastForegroundServiceEventMap.put(className, eventType);
- }
- } else if (eventType == FOREGROUND_SERVICE_START) {
- if (!isAppInUse()) {
- mLastTimeForegroundServiceUsed = timeStamp;
- }
- mLastForegroundServiceEventMap.put(className, eventType);
+ mForegroundServices.put(className, eventType);
+ break;
+ case FOREGROUND_SERVICE_STOP:
+ mForegroundServices.remove(className);
+ break;
+ default:
+ break;
}
}
/**
* Update the UsageStats by a activity or foreground service event.
- * @param className class name of a activity or foreground service, could be null to mark
- * END_OF_DAY or rollover.
+ * @param className class name of a activity or foreground service, could be null to if this
+ * is sent to all activities/services in this package.
* @param timeStamp Epoch timestamp in milliseconds.
* @param eventType event type as in {@link UsageEvents.Event}
+ * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken.
+ * if className is not an activity, instanceId is not used.
* @hide
*/
- public void update(String className, long timeStamp, int eventType) {
+ public void update(String className, long timeStamp, int eventType, int instanceId) {
switch(eventType) {
- case MOVE_TO_BACKGROUND:
- case MOVE_TO_FOREGROUND:
- updateForegroundActivity(className, timeStamp, eventType);
+ case ACTIVITY_RESUMED:
+ case ACTIVITY_PAUSED:
+ case ACTIVITY_STOPPED:
+ case ACTIVITY_DESTROYED:
+ updateActivity(className, timeStamp, eventType, instanceId);
break;
case END_OF_DAY:
- // END_OF_DAY means updating all activities.
- final int size = mLastForegroundActivityEventMap.size();
- for (int i = 0; i < size; i++) {
- final String name = mLastForegroundActivityEventMap.keyAt(i);
- updateForegroundActivity(name, timeStamp, eventType);
+ // END_OF_DAY updates all activities.
+ if (hasForegroundActivity()) {
+ incrementTimeUsed(timeStamp);
+ }
+ if (hasVisibleActivity()) {
+ incrementTimeVisible(timeStamp);
}
break;
- case CONTINUE_PREVIOUS_DAY:
- mLastTimeUsed = timeStamp;
- mLastForegroundActivityEventMap.put(className, eventType);
- break;
- case FOREGROUND_SERVICE_STOP:
case FOREGROUND_SERVICE_START:
+ case FOREGROUND_SERVICE_STOP:
updateForegroundService(className, timeStamp, eventType);
break;
case ROLLOVER_FOREGROUND_SERVICE:
- // ROLLOVER_FOREGROUND_SERVICE means updating all foreground services.
- final int size2 = mLastForegroundServiceEventMap.size();
- for (int i = 0; i < size2; i++) {
- final String name = mLastForegroundServiceEventMap.keyAt(i);
- updateForegroundService(name, timeStamp, eventType);
+ // ROLLOVER_FOREGROUND_SERVICE updates all foreground services.
+ if (anyForegroundServiceStarted()) {
+ incrementServiceTimeUsed(timeStamp);
}
break;
case CONTINUING_FOREGROUND_SERVICE:
mLastTimeForegroundServiceUsed = timeStamp;
- mLastForegroundServiceEventMap.put(className, eventType);
+ mForegroundServices.put(className, eventType);
+ break;
+ case FLUSH_TO_DISK:
+ // update usage of all active activities/services.
+ if (hasForegroundActivity()) {
+ incrementTimeUsed(timeStamp);
+ }
+ if (hasVisibleActivity()) {
+ incrementTimeVisible(timeStamp);
+ }
+ if (anyForegroundServiceStarted()) {
+ incrementServiceTimeUsed(timeStamp);
+ }
break;
default:
break;
}
mEndTimeStamp = timeStamp;
- if (eventType == MOVE_TO_FOREGROUND) {
+ if (eventType == ACTIVITY_RESUMED) {
mLaunchCount += 1;
}
}
@@ -455,8 +592,10 @@
dest.writeLong(mBeginTimeStamp);
dest.writeLong(mEndTimeStamp);
dest.writeLong(mLastTimeUsed);
+ dest.writeLong(mLastTimeVisible);
dest.writeLong(mLastTimeForegroundServiceUsed);
dest.writeLong(mTotalTimeInForeground);
+ dest.writeLong(mTotalTimeVisible);
dest.writeLong(mTotalTimeForegroundServiceUsed);
dest.writeInt(mLaunchCount);
dest.writeInt(mAppLaunchCount);
@@ -477,21 +616,26 @@
}
dest.writeBundle(allCounts);
- final Bundle foregroundActivityEventBundle = new Bundle();
- final int foregroundEventSize = mLastForegroundActivityEventMap.size();
- for (int i = 0; i < foregroundEventSize; i++) {
- foregroundActivityEventBundle.putInt(mLastForegroundActivityEventMap.keyAt(i),
- mLastForegroundActivityEventMap.valueAt(i));
- }
- dest.writeBundle(foregroundActivityEventBundle);
+ writeSparseIntArray(dest, mActivities);
+ dest.writeBundle(eventMapToBundle(mForegroundServices));
+ }
- final Bundle foregroundServiceEventBundle = new Bundle();
- final int foregroundServiceEventSize = mLastForegroundServiceEventMap.size();
- for (int i = 0; i < foregroundServiceEventSize; i++) {
- foregroundServiceEventBundle.putInt(mLastForegroundServiceEventMap.keyAt(i),
- mLastForegroundServiceEventMap.valueAt(i));
+ private void writeSparseIntArray(Parcel dest, SparseIntArray arr) {
+ final int size = arr.size();
+ dest.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ dest.writeInt(arr.keyAt(i));
+ dest.writeInt(arr.valueAt(i));
}
- dest.writeBundle(foregroundServiceEventBundle);
+ }
+
+ private Bundle eventMapToBundle(ArrayMap<String, Integer> eventMap) {
+ final Bundle bundle = new Bundle();
+ final int size = eventMap.size();
+ for (int i = 0; i < size; i++) {
+ bundle.putInt(eventMap.keyAt(i), eventMap.valueAt(i));
+ }
+ return bundle;
}
public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -502,8 +646,10 @@
stats.mBeginTimeStamp = in.readLong();
stats.mEndTimeStamp = in.readLong();
stats.mLastTimeUsed = in.readLong();
+ stats.mLastTimeVisible = in.readLong();
stats.mLastTimeForegroundServiceUsed = in.readLong();
stats.mTotalTimeInForeground = in.readLong();
+ stats.mTotalTimeVisible = in.readLong();
stats.mTotalTimeForegroundServiceUsed = in.readLong();
stats.mLaunchCount = in.readInt();
stats.mAppLaunchCount = in.readInt();
@@ -527,12 +673,21 @@
}
}
}
- readBundleToEventMap(stats.mLastForegroundActivityEventMap, in.readBundle());
- readBundleToEventMap(stats.mLastForegroundServiceEventMap, in.readBundle());
+ readSparseIntArray(in, stats.mActivities);
+ readBundleToEventMap(in.readBundle(), stats.mForegroundServices);
return stats;
}
- private void readBundleToEventMap(ArrayMap<String, Integer> eventMap, Bundle bundle) {
+ private void readSparseIntArray(Parcel in, SparseIntArray arr) {
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ final int key = in.readInt();
+ final int value = in.readInt();
+ arr.put(key, value);
+ }
+ }
+
+ private void readBundleToEventMap(Bundle bundle, ArrayMap<String, Integer> eventMap) {
if (bundle != null) {
for (String className : bundle.keySet()) {
final int event = bundle.getInt(className);
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 1a656ab..2edad35 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -37,9 +37,12 @@
* @param component The component for which this event occurred.
* @param userId The user id to which the component belongs to.
* @param eventType The event that occurred. Valid values can be found at
- * {@link UsageEvents}
+ * {@link UsageEvents}
+ * @param instanceId For activity, hashCode of ActivityRecord's appToken.
+ * For non-activity, it is not used.
*/
- public abstract void reportEvent(ComponentName component, @UserIdInt int userId, int eventType);
+ public abstract void reportEvent(ComponentName component, @UserIdInt int userId, int eventType,
+ int instanceId);
/**
* Reports an event to the UsageStatsManager.
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index ecc8cd6..a251c00 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -42,6 +42,8 @@
ParceledListSlice getAllSessions(int userId);
ParceledListSlice getMySessions(String installerPackageName, int userId);
+ ParceledListSlice getStagedSessions();
+
void registerCallback(IPackageInstallerCallback callback, int userId);
void unregisterCallback(IPackageInstallerCallback callback);
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index cef21f6..04e15c7 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -38,9 +38,12 @@
void commit(in IntentSender statusReceiver, boolean forTransferred);
void transfer(in String packageName);
void abandon();
+
boolean isMultiPackage();
int[] getChildSessionIds();
void addChildSessionId(in int sessionId);
void removeChildSessionId(in int sessionId);
int getParentSessionId();
+
+ boolean isStaged();
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 07672d9..f81eb76 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -68,7 +68,14 @@
* {@link PackageInstaller.Session}, which any app can create. Once the session
* is created, the installer can stream one or more APKs into place until it
* decides to either commit or destroy the session. Committing may require user
- * intervention to complete the installation.
+ * intervention to complete the installation, unless the caller falls into one of the
+ * following categories, in which case the installation will complete automatically.
+ * <ul>
+ * <li>the device owner
+ * <li>the affiliated profile owner
+ * <li>the device owner delegated app with
+ * {@link android.app.admin.DevicePolicyManager#DELEGATION_PACKAGE_INSTALLATION}
+ * </ul>
* <p>
* Sessions can install brand new apps, upgrade existing apps, or add new splits
* into an existing app.
@@ -463,12 +470,26 @@
}
/**
+ * Return list of all staged install sessions.
+ */
+ public @NonNull List<SessionInfo> getStagedSessions() {
+ try {
+ // TODO: limit this to the mUserId?
+ return mInstaller.getStagedSessions().getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Uninstall the given package, removing it completely from the device. This
* method is available to:
* <ul>
* <li>the current "installer of record" for the package
* <li>the device owner
* <li>the affiliated profile owner
+ * <li>the device owner delegated app with
+ * {@link android.app.admin.DevicePolicyManager#DELEGATION_PACKAGE_INSTALLATION}
* </ul>
*
* @param packageName The package to uninstall.
@@ -779,6 +800,11 @@
* individual session IDs can be added with {@link #addChildSessionId(int)}
* and commit of the multi-package session will result in all child sessions
* being committed atomically.
+ * <p>
+ * If a package requires to be installed only at reboot, the session should
+ * be marked as a staged session by calling {@link SessionParams#setStaged()}
+ * with {@code true}. This can also apply to a multi-package session, in
+ * which case all the packages in the session will be applied at reboot.
*/
public static class Session implements Closeable {
/** {@hide} */
@@ -1105,6 +1131,17 @@
}
/**
+ * @return {@code true} if this session will be staged and applied at next reboot.
+ */
+ public boolean isStaged() {
+ try {
+ return mSession.isStaged();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @return the session ID of the multi-package session that this belongs to or
* {@link SessionInfo#INVALID_ID} if it does not belong to a multi-package session.
*/
@@ -1227,6 +1264,8 @@
public String installerPackageName;
/** {@hide} */
public boolean isMultiPackage;
+ /** {@hide} */
+ public boolean isStaged;
/**
* Construct parameters for a new package install session.
@@ -1257,6 +1296,7 @@
grantedRuntimePermissions = source.readStringArray();
installerPackageName = source.readString();
isMultiPackage = source.readBoolean();
+ isStaged = source.readBoolean();
}
/**
@@ -1471,6 +1511,17 @@
this.isMultiPackage = true;
}
+ /**
+ * Set this session to be staged to be installed at reboot.
+ *
+ * Staged sessions are scheduled to be installed at next reboot. Staged sessions can also be
+ * multi-package. In that case, if any of the children sessions fail to install at reboot,
+ * all the other children sessions are aborted as well.
+ */
+ public void setStaged() {
+ this.isStaged = true;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
@@ -1488,6 +1539,7 @@
pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
pw.printPair("installerPackageName", installerPackageName);
pw.printPair("isMultiPackage", isMultiPackage);
+ pw.printPair("isStaged", isStaged);
pw.println();
}
@@ -1514,6 +1566,7 @@
dest.writeStringArray(grantedRuntimePermissions);
dest.writeString(installerPackageName);
dest.writeBoolean(isMultiPackage);
+ dest.writeBoolean(isStaged);
}
public static final Parcelable.Creator<SessionParams>
@@ -1593,6 +1646,8 @@
/** {@hide} */
public boolean isMultiPackage;
/** {@hide} */
+ public boolean isStaged;
+ /** {@hide} */
public int parentSessionId = INVALID_ID;
/** {@hide} */
public int[] childSessionIds = NO_SESSIONS;
@@ -1625,6 +1680,7 @@
grantedRuntimePermissions = source.readStringArray();
installFlags = source.readInt();
isMultiPackage = source.readBoolean();
+ isStaged = source.readBoolean();
parentSessionId = source.readInt();
childSessionIds = source.createIntArray();
if (childSessionIds == null) {
@@ -1892,6 +1948,13 @@
}
/**
+ * Returns true if this session is a staged session which will be applied at next reboot.
+ */
+ public boolean isStaged() {
+ return isStaged;
+ }
+
+ /**
* Returns the parent multi-package session ID if this session belongs to one,
* {@link #INVALID_ID} otherwise.
*/
@@ -1935,6 +1998,7 @@
dest.writeStringArray(grantedRuntimePermissions);
dest.writeInt(installFlags);
dest.writeBoolean(isMultiPackage);
+ dest.writeBoolean(isStaged);
dest.writeInt(parentSessionId);
dest.writeIntArray(childSessionIds);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index da39b63..34cdfee 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3377,6 +3377,33 @@
@ApplicationInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
/**
+ * Retrieve all of the information we know about a particular
+ * package/application, for a specific user.
+ *
+ * @param packageName The full name (i.e. com.google.apps.contacts) of an
+ * application.
+ * @param flags Additional option flags to modify the data returned.
+ * @return An {@link ApplicationInfo} containing information about the
+ * package. If flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if
+ * the package is not found in the list of installed applications,
+ * the application information is retrieved from the list of
+ * uninstalled applications (which includes installed applications
+ * as well as applications with data directory i.e. applications
+ * which had been deleted with {@code DONT_DELETE_DATA} flag set).
+ * @throws NameNotFoundException if a package with the given name cannot be
+ * found on the system.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
+ @ApplicationInfoFlags int flags, @NonNull UserHandle user)
+ throws NameNotFoundException {
+ return getApplicationInfoAsUser(packageName, flags, user.getIdentifier());
+ }
+
+ /**
* Retrieve all of the information we know about a particular activity
* class.
*
@@ -4203,6 +4230,32 @@
@ResolveInfoFlags int flags, @UserIdInt int userId);
/**
+ * Retrieve all activities that can be performed for the given intent, for a
+ * specific user.
+ *
+ * @param intent The desired intent as per resolveActivity().
+ * @param flags Additional option flags to modify the data returned. The
+ * most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
+ * resolution to only those activities that support the
+ * {@link android.content.Intent#CATEGORY_DEFAULT}. Or, set
+ * {@link #MATCH_ALL} to prevent any filtering of the results.
+ * @param user The user being queried.
+ * @return Returns a List of ResolveInfo objects containing one entry for
+ * each matching activity, ordered from best to worst. In other
+ * words, the first item is what would be returned by
+ * {@link #resolveActivity}. If there are no matching activities, an
+ * empty list is returned.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
+ @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+ return queryIntentActivitiesAsUser(intent, flags, user.getIdentifier());
+ }
+
+ /**
* Retrieve a set of activities that should be presented to the user as
* similar options. This is like {@link #queryIntentActivities}, except it
* also allows you to supply a list of more explicit Intents that you would
@@ -4334,6 +4387,27 @@
@ResolveInfoFlags int flags, @UserIdInt int userId);
/**
+ * Retrieve all services that can match the given intent for a given user.
+ *
+ * @param intent The desired intent as per resolveService().
+ * @param flags Additional option flags to modify the data returned.
+ * @param user The user being queried.
+ * @return Returns a List of ResolveInfo objects containing one entry for
+ * each matching service, ordered from best to worst. In other
+ * words, the first item is what would be returned by
+ * {@link #resolveService}. If there are no matching services, an
+ * empty list or null is returned.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
+ @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+ return queryIntentServicesAsUser(intent, flags, user.getIdentifier());
+ }
+
+ /**
* Retrieve all providers that can match the given intent.
*
* @param intent An intent containing all of the desired specification
@@ -4355,6 +4429,26 @@
* @param intent An intent containing all of the desired specification
* (action, data, type, category, and/or component).
* @param flags Additional option flags to modify the data returned.
+ * @param user The user being queried.
+ * @return Returns a List of ResolveInfo objects containing one entry for
+ * each matching provider, ordered from best to worst. If there are
+ * no matching services, an empty list or null is returned.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public List<ResolveInfo> queryIntentContentProvidersAsUser(@NonNull Intent intent,
+ @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+ return queryIntentContentProvidersAsUser(intent, flags, user.getIdentifier());
+ }
+
+ /**
+ * Retrieve all providers that can match the given intent.
+ *
+ * @param intent An intent containing all of the desired specification
+ * (action, data, type, category, and/or component).
+ * @param flags Additional option flags to modify the data returned.
* @return Returns a List of ResolveInfo objects containing one entry for
* each matching provider, ordered from best to worst. If there are
* no matching services, an empty list or null is returned.
@@ -6448,7 +6542,7 @@
*
* @hide
*/
- @SystemApi
+ @TestApi
public String getWellbeingPackageName() {
throw new UnsupportedOperationException(
"getWellbeingPackageName not implemented in subclass");
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 43c0222..5db9f50 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -54,6 +54,7 @@
public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
public static final int PACKAGE_WELLBEING = 7;
+ public static final int PACKAGE_DOCUMENTER = 8;
@IntDef(value = {
PACKAGE_SYSTEM,
PACKAGE_SETUP_WIZARD,
@@ -63,6 +64,7 @@
PACKAGE_SYSTEM_TEXT_CLASSIFIER,
PACKAGE_PERMISSION_CONTROLLER,
PACKAGE_WELLBEING,
+ PACKAGE_DOCUMENTER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface KnownPackage {}
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 20997d6..bb8c92d 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -192,6 +192,17 @@
@TestApi
public static final int PROTECTION_FLAG_WELLBEING = 0x20000;
+ /**
+ * Additional flag for {@link #protectionLevel}, corresponding to the
+ * {@code documenter} value of {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_DOCUMENTER = 0x40000;
+
+
/** @hide */
@IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
PROTECTION_FLAG_PRIVILEGED,
@@ -209,6 +220,7 @@
PROTECTION_FLAG_VENDOR_PRIVILEGED,
PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
PROTECTION_FLAG_WELLBEING,
+ PROTECTION_FLAG_DOCUMENTER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionFlags {}
@@ -401,6 +413,9 @@
if ((level & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0) {
protLevel += "|wellbeing";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
+ protLevel += "|documenter";
+ }
return protLevel;
}
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index f220205..4fc7f5d 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -143,7 +143,6 @@
/**
* contains statistics about a database
*/
- @TestApi
public static class DbStats {
/** name of the database */
public String dbName;
@@ -175,7 +174,6 @@
* return all pager and database stats for the current process.
* @return {@link PagerStats}
*/
- @TestApi
public static PagerStats getDatabaseInfo() {
PagerStats stats = new PagerStats();
nativeGetPagerStats(stats);
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index 1d9330d..9d37d99 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -28,6 +28,21 @@
*/
public interface BiometricFaceConstants {
//
+ // Accessibility constants
+ //
+ /**
+ * Require the user to look at the device during enrollment and
+ * authentication. Note this is to accommodate people who have limited
+ * vision.
+ */
+ public static final int FEATURE_REQUIRE_ATTENTION = 1;
+ /**
+ * Require a diverse set of poses during enrollment. Note this is to
+ * accommodate people with limited mobility.
+ */
+ public static final int FEATURE_REQUIRE_REQUIRE_DIVERSITY = 2;
+
+ //
// Error messages from face authentication hardware during initialization, enrollment,
// authentication or removal. Must agree with the list in HAL h file
//
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 9d61f02..1af9cde 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -16,6 +16,7 @@
package android.hardware.display;
+import android.annotation.Nullable;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.PowerManager;
@@ -195,6 +196,44 @@
public abstract void onOverlayChanged();
/**
+ * Get the attributes available for display color sampling.
+ * @param displayId id of the display to collect the sample from.
+ *
+ * @return The attributes the display supports, or null if sampling is not supported.
+ */
+ @Nullable
+ public abstract DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributes(
+ int displayId);
+
+ /**
+ * Enable or disable the collection of color samples.
+ *
+ * @param displayId id of the display to collect the sample from.
+ * @param componentMask a bitmask of the color channels to collect samples for, or zero for all
+ * available.
+ * @param maxFrames maintain a ringbuffer of the last maxFrames.
+ * @param enable True to enable, False to disable.
+ *
+ * @return True if sampling was enabled, false if failure.
+ */
+ public abstract boolean setDisplayedContentSamplingEnabled(
+ int displayId, boolean enable, int componentMask, int maxFrames);
+
+ /**
+ * Accesses the color histogram statistics of displayed frames on devices that support sampling.
+ *
+ * @param displayId id of the display to collect the sample from
+ * @param maxFrames limit the statistics to the last maxFrames number of frames.
+ * @param timestamp discard statistics that were collected prior to timestamp, where timestamp
+ * is given as CLOCK_MONOTONIC.
+ * @return The statistics representing a histogram of the color distribution of the frames
+ * displayed on-screen, or null if sampling is not supported.
+ */
+ @Nullable
+ public abstract DisplayedContentSample getDisplayedContentSample(
+ int displayId, long maxFrames, long timestamp);
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/display/DisplayedContentSample.java b/core/java/android/hardware/display/DisplayedContentSample.java
new file mode 100644
index 0000000..0610377
--- /dev/null
+++ b/core/java/android/hardware/display/DisplayedContentSample.java
@@ -0,0 +1,94 @@
+/*
+ * 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.hardware.display;
+
+/**
+ * @hide
+ */
+public final class DisplayedContentSample {
+ private long mNumFrames;
+ private long[] mSamplesComponent0;
+ private long[] mSamplesComponent1;
+ private long[] mSamplesComponent2;
+ private long[] mSamplesComponent3;
+
+ /**
+ * Construct an object representing a color histogram of pixels that were displayed on screen.
+ *
+ * @param numFrames The number of frames represented by this sample.
+ * @param mSamplesComponent0 is a histogram counting how many times a pixel of a given value
+ * was displayed onscreen for FORMAT_COMPONENT_0. The buckets of the histogram are evenly
+ * weighted, the number of buckets is device specific.
+ * eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that 10 red pixels were
+ * displayed onscreen in range 0x00->0x3F, 6 red pixels were displayed onscreen in range
+ * 0x40->0x7F, etc.
+ * @param mSamplesComponent1 is the same sample definition as sampleComponent0, but for the
+ * second component of format.
+ * @param mSamplesComponent2 is the same sample definition as sampleComponent0, but for the
+ * third component of format.
+ * @param mSamplesComponent3 is the same sample definition as sampleComponent0, but for the
+ * fourth component of format.
+ */
+ public DisplayedContentSample(long numFrames,
+ long[] sampleComponent0,
+ long[] sampleComponent1,
+ long[] sampleComponent2,
+ long[] sampleComponent3) {
+ mNumFrames = numFrames;
+ mSamplesComponent0 = sampleComponent0;
+ mSamplesComponent1 = sampleComponent1;
+ mSamplesComponent2 = sampleComponent2;
+ mSamplesComponent3 = sampleComponent3;
+ }
+
+ public enum ColorComponent {
+ CHANNEL0,
+ CHANNEL1,
+ CHANNEL2,
+ CHANNEL3,
+ }
+
+ /**
+ * Returns a color histogram according to component channel.
+ *
+ * @param component the component to return, according to the PixelFormat ordering
+ * (eg, for RGBA, CHANNEL0 is R, CHANNEL1 is G, etc).
+ *
+ * @return an evenly weighted histogram counting how many times a pixel was
+ * displayed onscreen that fell into the corresponding bucket, with the first entry
+ * corresponding to the normalized 0.0 value, and the last corresponding to the 1.0
+ * value for that PixelFormat component.
+ */
+ public long[] getSampleComponent(ColorComponent component) {
+ switch (component) {
+ case CHANNEL0: return mSamplesComponent0;
+ case CHANNEL1: return mSamplesComponent1;
+ case CHANNEL2: return mSamplesComponent2;
+ case CHANNEL3: return mSamplesComponent3;
+ default: throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Return the number of frames this sample was collected over.
+ *
+ * @return the number of frames that this sample was collected over.
+ */
+ public long getNumFrames() {
+ return mNumFrames;
+ }
+}
diff --git a/core/java/android/hardware/display/DisplayedContentSamplingAttributes.java b/core/java/android/hardware/display/DisplayedContentSamplingAttributes.java
new file mode 100644
index 0000000..aad68d9
--- /dev/null
+++ b/core/java/android/hardware/display/DisplayedContentSamplingAttributes.java
@@ -0,0 +1,67 @@
+/*
+ * 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.hardware.display;
+
+/**
+ * @hide
+ */
+public final class DisplayedContentSamplingAttributes {
+ private int mPixelFormat;
+ private int mDataspace;
+ private int mComponentMask;
+
+ /* Creates the attributes reported by the display hardware about what capabilities
+ * are present.
+ *
+ * NOTE: the format and ds constants must match the values from graphics/common/x.x/types.hal
+ * @param format the format that the display hardware samples in.
+ * @param ds the dataspace in use when sampling.
+ * @param componentMask a mask of which of the format components are supported.
+ */
+ public DisplayedContentSamplingAttributes(int format, int ds, int componentMask) {
+ mPixelFormat = format;
+ mDataspace = ds;
+ mComponentMask = componentMask;
+ }
+
+ /* Returns the pixel format that the display hardware uses when sampling.
+ *
+ * NOTE: the returned constant matches the values from graphics/common/x.x/types.hal
+ * @return the format that the samples were collected in.
+ */
+ public int getPixelFormat() {
+ return mPixelFormat;
+ }
+
+ /* Returns the dataspace that the display hardware uses when sampling.
+ *
+ * NOTE: the returned constant matches the values from graphics/common/x.x/types.hal
+ * @return the dataspace that the samples were collected in.
+ */
+ public int getDataspace() {
+ return mDataspace;
+ }
+
+ /* Returns a mask of which components can be collected by the sampling engine.
+ *
+ * @return a mask of the components which are supported by the engine. The lowest
+ * bit corresponds to the lowest component (ie, 0x1 corresponds to A for RGBA).
+ */
+ public int getComponentMask() {
+ return mComponentMask;
+ }
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 322863a..bac23b3 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -207,11 +207,8 @@
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void enroll(byte[] token, CancellationSignal cancel, int flags,
- int userId, EnrollmentCallback callback) {
- if (userId == UserHandle.USER_CURRENT) {
- userId = getCurrentUserId();
- }
+ public void enroll(byte[] token, CancellationSignal cancel,
+ EnrollmentCallback callback, int[] disabledFeatures) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
@@ -228,8 +225,8 @@
if (mService != null) {
try {
mEnrollmentCallback = callback;
- mService.enroll(mToken, token, userId, mServiceReceiver, flags,
- mContext.getOpPackageName());
+ mService.enroll(mToken, token, mServiceReceiver,
+ mContext.getOpPackageName(), disabledFeatures);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
@@ -284,10 +281,10 @@
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void setRequireAttention(boolean requireAttention, byte[] token) {
+ public void setFeature(int feature, boolean enabled, byte[] token) {
if (mService != null) {
try {
- mService.setRequireAttention(requireAttention, token);
+ mService.setFeature(feature, enabled, token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -298,11 +295,11 @@
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public boolean getRequireAttention(byte[] token) {
+ public boolean getFeature(int feature) {
boolean result = true;
if (mService != null) {
try {
- mService.getRequireAttention(token);
+ result = mService.getFeature(feature);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index a15dcec..a1c88f8 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -50,8 +50,8 @@
int callingUid, int callingPid, int callingUserId, boolean fromClient);
// Start face enrollment
- void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
- int flags, String opPackageName);
+ void enroll(IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver,
+ String opPackageName, in int [] disabledFeatures);
// Cancel enrollment in progress
void cancelEnrollment(IBinder token);
@@ -98,9 +98,9 @@
// Enumerate all faces
void enumerate(IBinder token, int userId, IFaceServiceReceiver receiver);
- int setRequireAttention(boolean requireAttention, in byte [] token);
+ int setFeature(int feature, boolean enabled, in byte [] token);
- boolean getRequireAttention(in byte [] token);
+ boolean getFeature(int feature);
void userActivity();
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 0c44a56..8a5f43d 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -931,7 +931,7 @@
* Returns a transport-specific information container. The application may cast this
* container to a concrete sub-class based on its knowledge of the network request. The
* application should be able to deal with a {@code null} return value or an invalid case,
- * e.g. use {@code instanceof} operation to verify expected type.
+ * e.g. use {@code instanceof} operator to verify expected type.
*
* @return A concrete implementation of the {@link TransportInfo} class or null if not
* available for the network.
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 22dd4fc..bbf8f97 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -274,7 +274,6 @@
* Changes only take effect during subsequent calls to
* {@link #tagSocket(Socket)}.
*/
- @SystemApi
@SuppressLint("Doclava125")
public static void setThreadStatsUid(int uid) {
NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
@@ -313,7 +312,6 @@
*
* @see #setThreadStatsUid(int)
*/
- @SystemApi
@SuppressLint("Doclava125")
public static void clearThreadStatsUid() {
NetworkManagementSocketTagger.setThreadSocketStatsUid(-1);
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index f9b6dfc..280dad0 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -16,7 +16,6 @@
package android.net.http;
-import android.annotation.SystemApi;
import android.security.net.config.UserCertificateSource;
import com.android.org.conscrypt.TrustManagerImpl;
@@ -133,7 +132,6 @@
* Returns {@code true} if the TrustManager uses the same trust configuration for the provided
* hostnames.
*/
- @SystemApi
public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
if (mIsSameTrustConfiguration == null) {
return true;
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 28ea553..567efa7 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -66,6 +66,9 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
@@ -717,6 +720,57 @@
}
/**
+ * Compute the digest of the given file using the requested algorithm.
+ *
+ * @param algorithm Any valid algorithm accepted by
+ * {@link MessageDigest#getInstance(String)}.
+ * @hide
+ */
+ public static byte[] digest(@NonNull File file, @NonNull String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ try (FileInputStream in = new FileInputStream(file)) {
+ return digest(in, algorithm);
+ }
+ }
+
+ /**
+ * Compute the digest of the given file using the requested algorithm.
+ *
+ * @param algorithm Any valid algorithm accepted by
+ * {@link MessageDigest#getInstance(String)}.
+ * @hide
+ */
+ public static byte[] digest(@NonNull InputStream in, @NonNull String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ // TODO: implement kernel optimizations
+ return digestInternalUserspace(in, algorithm);
+ }
+
+ /**
+ * Compute the digest of the given file using the requested algorithm.
+ *
+ * @param algorithm Any valid algorithm accepted by
+ * {@link MessageDigest#getInstance(String)}.
+ * @hide
+ */
+ public static byte[] digest(FileDescriptor fd, String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ // TODO: implement kernel optimizations
+ return digestInternalUserspace(new FileInputStream(fd), algorithm);
+ }
+
+ private static byte[] digestInternalUserspace(InputStream in, String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ final MessageDigest digest = MessageDigest.getInstance(algorithm);
+ try (DigestInputStream digestStream = new DigestInputStream(in, digest)) {
+ final byte[] buffer = new byte[8192];
+ while (digestStream.read(buffer) != -1) {
+ }
+ }
+ return digest.digest();
+ }
+
+ /**
* Delete older files in a directory until only those matching the given
* constraints remain.
*
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 7abe913..124d7b1 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -16,7 +16,6 @@
package android.os;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -71,7 +70,7 @@
*/
public void setup(Context context, Bundle coreSettings) {
setupGpuLayers(context, coreSettings);
- setupAngle(context, context.getPackageName());
+ setupAngle(context, coreSettings, context.getPackageName());
chooseDriver(context, coreSettings);
}
@@ -213,10 +212,9 @@
}
- private static List<String> getGlobalSettingsString(Context context, String globalSetting) {
+ private static List<String> getGlobalSettingsString(Bundle bundle, String globalSetting) {
List<String> valueList = null;
- ContentResolver contentResolver = context.getContentResolver();
- String settingsValue = Settings.Global.getString(contentResolver, globalSetting);
+ String settingsValue = bundle.getString(globalSetting);
if (settingsValue != null) {
valueList = new ArrayList<>(Arrays.asList(settingsValue.split(",")));
@@ -238,23 +236,18 @@
return -1;
}
- private static String getDriverForPkg(Context context, String packageName) {
- try {
- ContentResolver contentResolver = context.getContentResolver();
- int allUseAngle = Settings.Global.getInt(contentResolver,
- Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
- if (allUseAngle == 1) {
- return sDriverMap.get(OpenGlDriverChoice.ANGLE);
- }
- } catch (Settings.SettingNotFoundException e) {
- // Do nothing and move on
+ private static String getDriverForPkg(Bundle bundle, String packageName) {
+ String allUseAngle =
+ bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+ if ((allUseAngle != null) && allUseAngle.equals("1")) {
+ return sDriverMap.get(OpenGlDriverChoice.ANGLE);
}
List<String> globalSettingsDriverPkgs =
- getGlobalSettingsString(context,
+ getGlobalSettingsString(bundle,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS);
List<String> globalSettingsDriverValues =
- getGlobalSettingsString(context,
+ getGlobalSettingsString(bundle,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES);
// Make sure we have a good package name
@@ -285,8 +278,8 @@
/**
* Pass ANGLE details down to trigger enable logic
*/
- private void setupAngle(Context context, String packageName) {
- String devOptIn = getDriverForPkg(context, packageName);
+ private void setupAngle(Context context, Bundle bundle, String packageName) {
+ String devOptIn = getDriverForPkg(bundle, packageName);
if (DEBUG) {
Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 228fe7a..3de3494 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -32,10 +32,7 @@
/**
* Create and initialize a HwBinder object and the native objects
* used to allow this to participate in hwbinder transactions.
- *
- * @hide
*/
- @SystemApi
public HwBinder() {
native_setup();
@@ -44,7 +41,6 @@
mNativeContext);
}
- /** @hide */
@Override
public final native void transact(
int code, HwParcel request, HwParcel reply, int flags)
@@ -57,10 +53,7 @@
* @param request parceled transaction
* @param reply object to parcel reply into
* @param flags transaction flags to be chosen by wire protocol
- *
- * @hide
*/
- @SystemApi
public abstract void onTransact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
@@ -69,9 +62,7 @@
* Registers this service with the hwservicemanager.
*
* @param serviceName instance name of the service
- * @hide
*/
- @SystemApi
public native final void registerService(String serviceName)
throws RemoteException;
@@ -81,9 +72,7 @@
* @param iface fully-qualified interface name for example foo.bar@1.3::IBaz
* @param serviceName the instance name of the service for example default.
* @throws NoSuchElementException when the service is unavailable
- * @hide
*/
- @SystemApi
public static final IHwBinder getService(
String iface,
String serviceName)
@@ -96,9 +85,7 @@
* @param serviceName the instance name of the service for example default.
* @param retry whether to wait for the service to start if it's not already started
* @throws NoSuchElementException when the service is unavailable
- * @hide
*/
- @SystemApi
public static native final IHwBinder getService(
String iface,
String serviceName,
@@ -112,9 +99,7 @@
* @param maxThreads total number of threads to create (includes this thread if
* callerWillJoin is true)
* @param callerWillJoin whether joinRpcThreadpool will be called in advance
- * @hide
*/
- @SystemApi
public static native final void configureRpcThreadpool(
long maxThreads, boolean callerWillJoin);
@@ -124,10 +109,7 @@
* a threadpool with callerWillJoin true and then registering
* the provided service if this thread doesn't need to do
* anything else.
- *
- * @hide
*/
- @SystemApi
public static native final void joinRpcThreadpool();
// Returns address of the "freeFunction".
@@ -155,10 +137,7 @@
* - tries to enable atracing (if enabled)
* - tries to enable coverage dumps (if running in VTS)
* - tries to enable record and replay (if running in VTS)
- *
- * @hide
*/
- @SystemApi
public static void enableInstrumentation() {
native_report_sysprop_change();
}
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index fbdf27e..249eb3a 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -28,10 +28,7 @@
* @param request parceled transaction
* @param reply object to parcel reply into
* @param flags transaction flags to be chosen by wire protocol
- *
- * @hide
*/
- @SystemApi
public void transact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
@@ -40,23 +37,19 @@
* Return as IHwInterface instance only if this implements descriptor.
*
* @param descriptor for example foo.bar@1.0::IBaz
- * @hide
*/
- @SystemApi
public IHwInterface queryLocalInterface(String descriptor);
/**
* Interface for receiving a callback when the process hosting a service
* has gone away.
*/
- @SystemApi
public interface DeathRecipient {
/**
* Callback for a registered process dying.
*
* @param cookie cookie this death recipient was registered with.
*/
- @SystemApi
public void serviceDied(long cookie);
}
@@ -67,13 +60,11 @@
* @param recipient callback object to be called on object death.
* @param cookie value to be given to callback on object death.
*/
- @SystemApi
public boolean linkToDeath(DeathRecipient recipient, long cookie);
/**
* Unregisters the death recipient from this binder.
*
* @param recipient callback to no longer recieve death notifications on this binder.
*/
- @SystemApi
public boolean unlinkToDeath(DeathRecipient recipient);
}
diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java
index 1d9e2b0..f9edd5b 100644
--- a/core/java/android/os/IHwInterface.java
+++ b/core/java/android/os/IHwInterface.java
@@ -23,6 +23,5 @@
/**
* @return the binder object that corresponds to this interface.
*/
- @SystemApi
public IHwBinder asBinder();
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index c9c4205..be8cf0e 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -18,7 +18,6 @@
package android.os;
import android.net.InterfaceConfiguration;
-import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.ITetheringStatsProvider;
import android.net.Network;
@@ -47,11 +46,6 @@
void unregisterObserver(INetworkManagementEventObserver obs);
/**
- * Retrieve an INetd to talk to netd.
- */
- INetd getNetdService();
-
- /**
* Returns a list of currently known network interfaces
*/
String[] listInterfaces();
diff --git a/core/java/android/os/SystemUpdateManager.java b/core/java/android/os/SystemUpdateManager.java
index ce3e2259..9146731 100644
--- a/core/java/android/os/SystemUpdateManager.java
+++ b/core/java/android/os/SystemUpdateManager.java
@@ -34,62 +34,51 @@
private static final String TAG = "SystemUpdateManager";
/** The status key of the system update info, expecting an int value. */
- @SystemApi
public static final String KEY_STATUS = "status";
/** The title of the current update, expecting a String value. */
- @SystemApi
public static final String KEY_TITLE = "title";
/** Whether it is a security update, expecting a boolean value. */
- @SystemApi
public static final String KEY_IS_SECURITY_UPDATE = "is_security_update";
/** The build fingerprint after installing the current update, expecting a String value. */
- @SystemApi
public static final String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint";
/** The security patch level after installing the current update, expecting a String value. */
- @SystemApi
public static final String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level";
/**
* The KEY_STATUS value that indicates there's no update status info available.
*/
- @SystemApi
public static final int STATUS_UNKNOWN = 0;
/**
* The KEY_STATUS value that indicates there's no pending update.
*/
- @SystemApi
public static final int STATUS_IDLE = 1;
/**
* The KEY_STATUS value that indicates an update is available for download, but pending user
* approval to start.
*/
- @SystemApi
public static final int STATUS_WAITING_DOWNLOAD = 2;
/**
* The KEY_STATUS value that indicates an update is in progress (i.e. downloading or installing
* has started).
*/
- @SystemApi
public static final int STATUS_IN_PROGRESS = 3;
/**
* The KEY_STATUS value that indicates an update is available for install.
*/
- @SystemApi
public static final int STATUS_WAITING_INSTALL = 4;
/**
* The KEY_STATUS value that indicates an update will be installed after a reboot. This applies
* to both of A/B and non-A/B OTAs.
*/
- @SystemApi
public static final int STATUS_WAITING_REBOOT = 5;
private final ISystemUpdateManager mService;
@@ -110,7 +99,6 @@
*
* @throws SecurityException if the caller is not allowed to read the info.
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_SYSTEM_UPDATE_INFO,
android.Manifest.permission.RECOVERY,
@@ -137,7 +125,6 @@
* @throws IllegalArgumentException if @link #KEY_STATUS} does not exist.
* @throws SecurityException if the caller is not allowed to update the info.
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.RECOVERY)
public void updateSystemUpdateInfo(PersistableBundle infoBundle) {
if (infoBundle == null || !infoBundle.containsKey(KEY_STATUS)) {
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 24c9c91..8f2826c 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -54,7 +54,6 @@
* Error code from the update engine. Values must agree with the ones in
* system/update_engine/common/error_code.h.
*/
- @SystemApi
public static final class ErrorCodeConstants {
public static final int SUCCESS = 0;
public static final int ERROR = 1;
@@ -74,7 +73,6 @@
* Update status code from the update engine. Values must agree with the
* ones in system/update_engine/client_library/include/update_engine/update_status.h.
*/
- @SystemApi
public static final class UpdateStatusConstants {
public static final int IDLE = 0;
public static final int CHECKING_FOR_UPDATE = 1;
@@ -95,7 +93,6 @@
/**
* Creates a new instance.
*/
- @SystemApi
public UpdateEngine() {
mUpdateEngine = IUpdateEngine.Stub.asInterface(
ServiceManager.getService(UPDATE_ENGINE_SERVICE));
@@ -106,7 +103,6 @@
* status change, and when the update completes. A handler can be supplied
* to control which thread runs the callback, or null.
*/
- @SystemApi
public boolean bind(final UpdateEngineCallback callback, final Handler handler) {
synchronized (mUpdateEngineCallbackLock) {
mUpdateEngineCallback = new IUpdateEngineCallback.Stub() {
@@ -150,7 +146,6 @@
/**
* Equivalent to {@code bind(callback, null)}.
*/
- @SystemApi
public boolean bind(final UpdateEngineCallback callback) {
return bind(callback, null);
}
@@ -183,7 +178,6 @@
* };
* </pre>
*/
- @SystemApi
public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) {
try {
mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs);
@@ -201,7 +195,6 @@
* <p>See {@link #suspend} for a way to temporarily stop an in-progress
* update with the ability to resume it later.
*/
- @SystemApi
public void cancel() {
try {
mUpdateEngine.cancel();
@@ -214,7 +207,6 @@
* Suspends an in-progress update. This can be undone by calling
* {@link #resume}.
*/
- @SystemApi
public void suspend() {
try {
mUpdateEngine.suspend();
@@ -226,7 +218,6 @@
/**
* Resumes a suspended update.
*/
- @SystemApi
public void resume() {
try {
mUpdateEngine.resume();
@@ -244,7 +235,6 @@
* {@code UPDATED_NEED_REBOOT}, so your callback can remove any outstanding
* notification that rebooting into the new system is possible.
*/
- @SystemApi
public void resetStatus() {
try {
mUpdateEngine.resetStatus();
@@ -256,7 +246,6 @@
/**
* Unbinds the last bound callback function.
*/
- @SystemApi
public boolean unbind() {
synchronized (mUpdateEngineCallbackLock) {
if (mUpdateEngineCallback == null) {
@@ -281,7 +270,6 @@
* @param payloadMetadataFilename the location of the metadata without the
* {@code file://} prefix.
*/
- @SystemApi
public boolean verifyPayloadMetadata(String payloadMetadataFilename) {
try {
return mUpdateEngine.verifyPayloadApplicable(payloadMetadataFilename);
diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java
index afff60a..f07294e 100644
--- a/core/java/android/os/UpdateEngineCallback.java
+++ b/core/java/android/os/UpdateEngineCallback.java
@@ -37,7 +37,6 @@
* be one of the values from {@link UpdateEngine.UpdateStatusConstants},
* and {@code percent} will be valid [TODO: in which cases?].
*/
- @SystemApi
public abstract void onStatusUpdate(int status, float percent);
/**
@@ -45,6 +44,5 @@
* unsuccessfully. The value of {@code errorCode} will be one of the
* values from {@link UpdateEngine.ErrorCodeConstants}.
*/
- @SystemApi
public abstract void onPayloadApplicationComplete(int errorCode);
}
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 865b8f8..c167ea1 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.UnsupportedAppUsage;
@@ -41,6 +42,8 @@
import android.text.format.Time;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
/**
* <p>
* The contract between the calendar provider and applications. Contains
@@ -129,6 +132,13 @@
"android.provider.calendar.action.HANDLE_CUSTOM_EVENT";
/**
+ * Action used to help apps show calendar events in the managed profile.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_VIEW_WORK_CALENDAR_EVENT =
+ "android.provider.calendar.action.VIEW_WORK_CALENDAR_EVENT";
+
+ /**
* Intent Extras key: {@link EventsColumns#CUSTOM_APP_URI} for the event in
* the {@link #ACTION_HANDLE_CUSTOM_EVENT} intent
*/
@@ -153,6 +163,11 @@
public static final String EXTRA_EVENT_ALL_DAY = "allDay";
/**
+ * Intent Extras key: The id of an event.
+ */
+ public static final String EXTRA_EVENT_ID = "id";
+
+ /**
* This authority is used for writing to or querying from the calendar
* provider. Note: This is set at first run and cannot be changed without
* breaking apps that access the provider.
@@ -195,6 +210,43 @@
private CalendarContract() {}
/**
+ * Starts an activity to view calendar events in the managed profile.
+ *
+ * When this API is called, the system will attempt to start an activity
+ * in the managed profile with an intent targeting the same caller package.
+ * The intent will have its action set to
+ * {@link CalendarContract#ACTION_VIEW_WORK_CALENDAR_EVENT} and contain extras
+ * corresponding to the API's arguments. A calendar app intending to support
+ * cross profile events viewing should handle this intent, parse the arguments
+ * and show the appropriate UI.
+ *
+ * @param context the context.
+ * @param eventId the id of the event to be viewed. Will be put into {@link #EXTRA_EVENT_ID}
+ * field of the intent.
+ * @param start the start time of the event. Will be put into {@link #EXTRA_EVENT_BEGIN_TIME}
+ * field of the intent.
+ * @param end the end time of the event. Will be put into {@link #EXTRA_EVENT_END_TIME} field
+ * of the intent.
+ * @param allDay if the event is an all-day event. Will be put into
+ * {@link #EXTRA_EVENT_ALL_DAY} field of the intent.
+ * @param flags flags to be set on the intent via {@link Intent#setFlags}
+ * @return {@code true} if the activity is started successfully. {@code false} otherwise.
+ *
+ * @see #EXTRA_EVENT_ID
+ * @see #EXTRA_EVENT_BEGIN_TIME
+ * @see #EXTRA_EVENT_END_TIME
+ * @see #EXTRA_EVENT_ALL_DAY
+ */
+ public static boolean startViewCalendarEventInManagedProfile(@NonNull Context context,
+ long eventId, long start, long end, boolean allDay, int flags) {
+ Preconditions.checkNotNull(context, "Context is null");
+ final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ return dpm.startViewCalendarEventInManagedProfile(eventId, start,
+ end, allDay, flags);
+ }
+
+ /**
* Generic columns for use by sync adapters. The specific functions of these
* columns are private to the sync adapter. Other clients of the API should
* not attempt to either read or write this column. These columns are
@@ -695,7 +747,7 @@
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendars");
/**
- * The content:// style URL for querying Calendars table in the work profile. Appending a
+ * The content:// style URL for querying Calendars table in the managed profile. Appending a
* calendar id using {@link ContentUris#withAppendedId(Uri, long)} will
* specify a single calendar.
*
@@ -715,9 +767,9 @@
* projection of the query to this uri that are not contained in the above list.
*
* <p>This uri will return an empty cursor if the calling user is not a parent profile
- * of a work profile, or cross profile calendar is disabled in Settings, or this uri is
- * queried from a package that is not whitelisted by profile owner of the work profile via
- * {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+ * of a managed profile, or cross profile calendar is disabled in Settings, or this uri is
+ * queried from a package that is not whitelisted by profile owner of the managed profile
+ * via {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
*
* @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
* @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
@@ -1673,7 +1725,7 @@
Uri.parse("content://" + AUTHORITY + "/events");
/**
- * The content:// style URL for querying Events table in the work profile. Appending an
+ * The content:// style URL for querying Events table in the managed profile. Appending an
* event id using {@link ContentUris#withAppendedId(Uri, long)} will
* specify a single event.
*
@@ -1706,9 +1758,9 @@
* projection of the query to this uri that are not contained in the above list.
*
* <p>This uri will return an empty cursor if the calling user is not a parent profile
- * of a work profile, or cross profile calendar is disabled in Settings, or this uri is
- * queried from a package that is not whitelisted by profile owner of the work profile via
- * {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+ * of a managed profile, or cross profile calendar is disabled in Settings, or this uri is
+ * queried from a package that is not whitelisted by profile owner of the managed profile
+ * via {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
*
* @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
* @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
@@ -1896,7 +1948,7 @@
Uri.parse("content://" + AUTHORITY + "/instances/searchbyday");
/**
- * The content:// style URL for querying an instance range in the work profile.
+ * The content:// style URL for querying an instance range in the managed profile.
* It supports similar semantics as {@link #CONTENT_URI}.
*
* <p>The following columns plus the columns that are whitelisted by
@@ -1916,9 +1968,9 @@
* projection of the query to this uri that are not contained in the above list.
*
* <p>This uri will return an empty cursor if the calling user is not a parent profile
- * of a work profile, or cross profile calendar for the work profile is disabled in
+ * of a managed profile, or cross profile calendar for the managed profile is disabled in
* Settings, or this uri is queried from a package that is not whitelisted by
- * profile owner of the work profile via
+ * profile owner of the managed profile via
* {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
*
* @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
@@ -1929,7 +1981,7 @@
/**
* The content:// style URL for querying an instance range by Julian
- * Day in the work profile. It supports similar semantics as {@link #CONTENT_BY_DAY_URI}
+ * Day in the managed profile. It supports similar semantics as {@link #CONTENT_BY_DAY_URI}
* and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}.
*/
public static final Uri ENTERPRISE_CONTENT_BY_DAY_URI =
@@ -1937,7 +1989,7 @@
/**
* The content:// style URL for querying an instance range with a search
- * term in the work profile. It supports similar semantics as {@link #CONTENT_SEARCH_URI}
+ * term in the managed profile. It supports similar semantics as {@link #CONTENT_SEARCH_URI}
* and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}.
*/
public static final Uri ENTERPRISE_CONTENT_SEARCH_URI =
@@ -1945,7 +1997,7 @@
/**
* The content:// style URL for querying an instance range with a search
- * term in the work profile. It supports similar semantics as
+ * term in the managed profile. It supports similar semantics as
* {@link #CONTENT_SEARCH_BY_DAY_URI} and performs similar checks as
* {@link #ENTERPRISE_CONTENT_URI}.
*/
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index ff77228..a8726e9 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -244,18 +244,34 @@
* Get string array identifies the type or types of metadata returned
* using DocumentsContract#getDocumentMetadata.
*
- * @see #getDocumentMetadata(ContentResolver, Uri)
+ * @see #getDocumentMetadata(ContentInterface, Uri)
*/
public static final String METADATA_TYPES = "android:documentMetadataTypes";
/**
* Get Exif information using DocumentsContract#getDocumentMetadata.
*
- * @see #getDocumentMetadata(ContentResolver, Uri)
+ * @see #getDocumentMetadata(ContentInterface, Uri)
*/
public static final String METADATA_EXIF = "android:documentExif";
/**
+ * Get total count of all documents currently stored under the given
+ * directory tree. Only valid for {@link Document#MIME_TYPE_DIR} documents.
+ *
+ * @see #getDocumentMetadata(ContentInterface, Uri)
+ */
+ public static final String METADATA_TREE_COUNT = "android:metadataTreeCount";
+
+ /**
+ * Get total size of all documents currently stored under the given
+ * directory tree. Only valid for {@link Document#MIME_TYPE_DIR} documents.
+ *
+ * @see #getDocumentMetadata(ContentInterface, Uri)
+ */
+ public static final String METADATA_TREE_SIZE = "android:metadataTreeSize";
+
+ /**
* Constants related to a document, including {@link Cursor} column names
* and flags.
* <p>
@@ -519,7 +535,7 @@
* using DocumentsContract#getDocumentMetadata
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#getDocumentMetadata(ContentResolver, Uri)
+ * @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri)
*/
public static final int FLAG_SUPPORTS_METADATA = 1 << 14;
}
@@ -1259,6 +1275,12 @@
}
}
+ /** @removed */
+ public static Bitmap getDocumentThumbnail(ContentResolver content, Uri documentUri, Point size,
+ CancellationSignal signal) throws FileNotFoundException {
+ return getDocumentThumbnail((ContentInterface) content, documentUri, size, signal);
+ }
+
/**
* Create a new document with given MIME type and display name.
*
@@ -1285,6 +1307,11 @@
}
}
+ /** @removed */
+ public static Uri createDocument(ContentResolver content, Uri parentDocumentUri,
+ String mimeType, String displayName) throws FileNotFoundException {
+ return createDocument((ContentInterface) content, parentDocumentUri, mimeType, displayName);
+ }
/**
* Test if a document is descendant (child, grandchild, etc) from the given
@@ -1318,6 +1345,12 @@
}
}
+ /** @removed */
+ public static boolean isChildDocument(ContentResolver content, Uri parentDocumentUri,
+ Uri childDocumentUri) throws FileNotFoundException {
+ return isChildDocument((ContentInterface) content, parentDocumentUri, childDocumentUri);
+ }
+
/**
* Change the display name of an existing document.
* <p>
@@ -1349,6 +1382,12 @@
}
}
+ /** @removed */
+ public static Uri renameDocument(ContentResolver content, Uri documentUri,
+ String displayName) throws FileNotFoundException {
+ return renameDocument((ContentInterface) content, documentUri, displayName);
+ }
+
/**
* Delete the given document.
*
@@ -1371,6 +1410,12 @@
}
}
+ /** @removed */
+ public static boolean deleteDocument(ContentResolver content, Uri documentUri)
+ throws FileNotFoundException {
+ return deleteDocument((ContentInterface) content, documentUri);
+ }
+
/**
* Copies the given document.
*
@@ -1396,6 +1441,12 @@
}
}
+ /** @removed */
+ public static Uri copyDocument(ContentResolver content, Uri sourceDocumentUri,
+ Uri targetParentDocumentUri) throws FileNotFoundException {
+ return copyDocument((ContentInterface) content, sourceDocumentUri, targetParentDocumentUri);
+ }
+
/**
* Moves the given document under a new parent.
*
@@ -1423,6 +1474,13 @@
}
}
+ /** @removed */
+ public static Uri moveDocument(ContentResolver content, Uri sourceDocumentUri,
+ Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException {
+ return moveDocument((ContentInterface) content, sourceDocumentUri, sourceParentDocumentUri,
+ targetParentDocumentUri);
+ }
+
/**
* Removes the given document from a parent directory.
*
@@ -1450,6 +1508,12 @@
}
}
+ /** @removed */
+ public static boolean removeDocument(ContentResolver content, Uri documentUri,
+ Uri parentDocumentUri) throws FileNotFoundException {
+ return removeDocument((ContentInterface) content, documentUri, parentDocumentUri);
+ }
+
/**
* Ejects the given root. It throws {@link IllegalStateException} when ejection failed.
*
@@ -1467,6 +1531,11 @@
}
}
+ /** @removed */
+ public static void ejectRoot(ContentResolver content, Uri rootUri) {
+ ejectRoot((ContentInterface) content, rootUri);
+ }
+
/**
* Returns metadata associated with the document. The type of metadata returned
* is specific to the document type. For example the data returned for an image
@@ -1512,6 +1581,12 @@
}
}
+ /** @removed */
+ public static Bundle getDocumentMetadata(ContentResolver content, Uri documentUri)
+ throws FileNotFoundException {
+ return getDocumentMetadata((ContentInterface) content, documentUri);
+ }
+
/**
* Finds the canonical path from the top of the document tree.
*
@@ -1543,6 +1618,12 @@
}
}
+ /** @removed */
+ public static Path findDocumentPath(ContentResolver content, Uri treeUri)
+ throws FileNotFoundException {
+ return findDocumentPath((ContentInterface) content, treeUri);
+ }
+
/**
* Creates an intent for obtaining a web link for the specified document.
*
@@ -1616,6 +1697,12 @@
}
}
+ /** @removed */
+ public static IntentSender createWebLinkIntent(ContentResolver content, Uri uri,
+ Bundle options) throws FileNotFoundException {
+ return createWebLinkIntent((ContentInterface) content, uri, options);
+ }
+
/**
* Open the given image for thumbnail purposes, using any embedded EXIF
* thumbnail if available, and providing orientation hints from the parent
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9380695..93a5950 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13933,13 +13933,6 @@
/**
* Store a name/value pair into the database.
* <p>
- * The method takes an optional tag to associate with the setting which can be used to clear
- * only settings made by your package and associated with this tag by passing the tag to
- * {@link #resetToDefaults(ContentResolver, String)}. The value of this setting can be
- * overridden by future calls to this or other put methods, and the tag provided in those
- * calls, which may be null, will override the tag provided in this call. Any call to a put
- * method which does not accept a tag will effectively set the tag to null.
- * </p><p>
* Also the method takes an argument whether to make the value the default for this setting.
* If the system already specified a default value, then the one passed in here will
* <strong>not</strong> be set as the default.
@@ -13948,46 +13941,47 @@
* @param resolver to access the database with.
* @param name to store.
* @param value to associate with the name.
- * @param tag to associated with the setting.
* @param makeDefault whether to make the value the default one.
* @return true if the value was set, false on database errors.
*
- * @see #resetToDefaults(ContentResolver, String)
+ * @see #resetToDefaults(ContentResolver, int, String)
*
* @hide
*/
// TODO(b/117663715): require a new write permission restricted to a single source
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
- static boolean putString(@NonNull ContentResolver resolver,
- @NonNull String name, @Nullable String value, @Nullable String tag,
- boolean makeDefault) {
- return sNameValueCache.putStringForUser(resolver, name, value, tag, makeDefault,
+ static boolean putString(@NonNull ContentResolver resolver, @NonNull String name,
+ @Nullable String value, boolean makeDefault) {
+ return sNameValueCache.putStringForUser(resolver, name, value, null, makeDefault,
resolver.getUserId());
}
/**
- * Reset the settings to their defaults. This would reset <strong>only</strong> settings set
- * by the caller's package. Passing in the optional tag will reset only settings changed by
- * your package and associated with this tag.
+ * Reset the values to their defaults.
+ * <p>
+ * The method accepts an optional prefix parameter. If provided, only pairs with a name that
+ * starts with the exact prefix will be reset. Otherwise all will be reset.
*
* @param resolver Handle to the content resolver.
- * @param tag Optional tag which should be associated with the settings to reset.
+ * @param resetMode The reset mode to use.
+ * @param prefix Optionally, to limit which which pairs are reset.
*
- * @see #putString(ContentResolver, String, String, String, boolean)
+ * @see #putString(ContentResolver, String, String, boolean)
*
* @hide
*/
// TODO(b/117663715): require a new write permission restricted to a single source
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
- static void resetToDefaults(@NonNull ContentResolver resolver,
- @Nullable String tag) {
+ static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode,
+ @Nullable String prefix) {
try {
Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
- if (tag != null) {
- arg.putString(CALL_METHOD_TAG_KEY, tag);
+ arg.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, resetMode);
+ if (prefix != null) {
+ arg.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
}
- arg.putInt(CALL_METHOD_RESET_MODE_KEY, RESET_MODE_PACKAGE_DEFAULTS);
+ arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
CALL_METHOD_RESET_CONFIG, null, arg);
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 68a86f3..9aff281 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -32,7 +32,6 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
import android.util.Log;
import android.util.Pair;
@@ -172,7 +171,9 @@
mAutofillProxies.put(sessionId, proxy);
} else {
// TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
+ // TODO(b/111330312): also make sure to cover scenario on CTS test
if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
+ proxy.update(focusedId, focusedValue);
}
// TODO(b/111330312): set cancellation signal
final CancellationSignal cancellationSignal = null;
@@ -248,8 +249,10 @@
private final IFillCallback mCallback;
public final int taskId;
public final ComponentName componentName;
- public final AutofillId focusedId;
- public final AutofillValue focusedValue;
+ @GuardedBy("mLock")
+ private AutofillId mFocusedId;
+ @GuardedBy("mLock")
+ private AutofillValue mFocusedValue;
// Objects used to log metrics
private final long mRequestTime;
@@ -272,8 +275,8 @@
mCallback = callback;
this.taskId = taskId;
this.componentName = componentName;
- this.focusedId = focusedId;
- this.focusedValue = focusedValue;
+ this.mFocusedId = focusedId;
+ this.mFocusedValue = focusedValue;
this.mRequestTime = requestTime;
// TODO(b/111330312): linkToDeath
}
@@ -286,13 +289,13 @@
}
Rect rect;
try {
- rect = mClient.getViewCoordinates(focusedId);
+ rect = mClient.getViewCoordinates(mFocusedId);
} catch (RemoteException e) {
- Log.w(TAG, "Could not get coordinates for " + focusedId);
+ Log.w(TAG, "Could not get coordinates for " + mFocusedId);
return null;
}
if (rect == null) {
- if (DEBUG) Log.d(TAG, "getViewCoordinates(" + focusedId + ") returned null");
+ if (DEBUG) Log.d(TAG, "getViewCoordinates(" + mFocusedId + ") returned null");
return null;
}
mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
@@ -325,6 +328,28 @@
}
}
+ private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
+ synchronized (mLock) {
+ // TODO(b/111330312): should we close the popupwindow if the focused id changed?
+ mFocusedId = focusedId;
+ mFocusedValue = focusedValue;
+ }
+ }
+
+ @NonNull
+ public AutofillId getFocusedId() {
+ synchronized (mLock) {
+ return mFocusedId;
+ }
+ }
+
+ @NonNull
+ public AutofillValue getFocusedValue() {
+ synchronized (mLock) {
+ return mFocusedValue;
+ }
+ }
+
// Used (mostly) for metrics.
public void report(@ReportEvent int event) {
switch (event) {
@@ -372,9 +397,9 @@
pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
pw.print(prefix); pw.print("component: ");
pw.println(componentName.flattenToShortString());
- pw.print(prefix); pw.print("focusedId: "); pw.println(focusedId);
- if (focusedValue != null) {
- pw.print(prefix); pw.print("focusedValue: "); pw.println(focusedValue);
+ pw.print(prefix); pw.print("focusedId: "); pw.println(mFocusedId);
+ if (mFocusedValue != null) {
+ pw.print(prefix); pw.print("focusedValue: "); pw.println(mFocusedValue);
}
pw.print(prefix); pw.print("client: "); pw.println(mClient);
final String prefix2 = prefix + " ";
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 0546465..620ec59 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -15,10 +15,13 @@
*/
package android.service.autofill.augmented;
+import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
+import android.util.Log;
/**
* Callback used to indicate at {@link FillRequest} has been fulfilled.
@@ -27,6 +30,9 @@
*/
@SystemApi
public final class FillCallback {
+
+ private static final String TAG = FillCallback.class.getSimpleName();
+
private final AutofillProxy mProxy;
FillCallback(@NonNull AutofillProxy proxy) {
@@ -40,7 +46,11 @@
* could not provide autofill for the request.
*/
public void onSuccess(@Nullable FillResponse response) {
+ if (DEBUG) Log.d(TAG, "onSuccess(): " + response);
+
mProxy.report(AutofillProxy.REPORT_EVENT_ON_SUCCESS);
+ if (response == null) return;
+
final FillWindow fillWindow = response.getFillWindow();
if (fillWindow != null) {
fillWindow.show();
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index fd75b15..57d2cc8 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -58,7 +58,7 @@
*/
@NonNull
public AutofillId getFocusedId() {
- return mProxy.focusedId;
+ return mProxy.getFocusedId();
}
/**
@@ -66,7 +66,7 @@
*/
@NonNull
public AutofillValue getFocusedValue() {
- return mProxy.focusedValue;
+ return mProxy.getFocusedValue();
}
/**
@@ -82,6 +82,6 @@
@Override
public String toString() {
return "FillRequest[act=" + getActivityComponent().flattenToShortString()
- + ", id=" + mProxy.focusedId + "]";
+ + ", id=" + mProxy.getFocusedId() + "]";
}
}
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index 7064b6f..1ecfab4 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -102,6 +102,8 @@
// TODO(b/111330312): add methods to disable app / activity, either here or on manager
}
+ // TODO(b/111330312): implement to String
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index e930f40..568ca0f 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -71,10 +71,8 @@
* @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
+ * @param preciseCarrierId precise carrier identifier
+ * {@link TelephonyManager#getSimPreciseCarrierId()}
*/
public CarrierIdentifier(String mcc, String mnc, @Nullable String spn,
@Nullable String imsi, @Nullable String gid1, @Nullable String gid2,
@@ -155,16 +153,16 @@
}
/**
- * Get the carrier id {@link TelephonyManager#getSimCarrierId() }
- * @hide
+ * Returns the carrier id.
+ * @see TelephonyManager#getSimCarrierId()
*/
public int getCarrierId() {
return mCarrierId;
}
/**
- * Get the precise carrier id {@link TelephonyManager#getSimPreciseCarrierId()}
- * @hide
+ * Returns the precise carrier id.
+ * @see TelephonyManager#getSimPreciseCarrierId()
*/
public int getPreciseCarrierId() {
return mPreciseCarrierId;
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 518f8ed..de532b7 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -80,11 +80,18 @@
* Data type: int, one of importance values e.g.
* {@link android.app.NotificationManager#IMPORTANCE_MIN}.
*
- * If used from
- * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)}, it can
- * block a notification from appearing or silence it. If used from
- * {@link NotificationAssistantService#adjustNotification(Adjustment)}, it can visually
- * demote a notification.
+ * <p> If used from
+ * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)}, and
+ * received before the notification is posted, it can block a notification from appearing or
+ * silence it. Importance adjustments received too late from
+ * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)} will be
+ * ignored.
+ * </p>
+ * <p>If used from
+ * {@link NotificationAssistantService#adjustNotification(Adjustment)}, it can
+ * visually demote or cancel a notification, but use this with care if they notification was
+ * recently posted because the notification may already have made noise.
+ * </p>
*/
public static final String KEY_IMPORTANCE = "key_importance";
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 5a7a83f..af7e93e 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -17,7 +17,6 @@
package android.service.notification;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.content.Context;
import android.net.Uri;
import android.os.Parcel;
@@ -35,7 +34,6 @@
*/
public final class Condition implements Parcelable {
- @SystemApi
public static final String SCHEME = "condition";
/** @hide */
@@ -59,14 +57,10 @@
*/
public static final int STATE_TRUE = 1;
- @SystemApi
public static final int STATE_UNKNOWN = 2;
- @SystemApi
public static final int STATE_ERROR = 3;
- @SystemApi
public static final int FLAG_RELEVANT_NOW = 1 << 0;
- @SystemApi
public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
/**
@@ -81,9 +75,7 @@
*/
public final String summary;
- @SystemApi
public final String line1;
- @SystemApi
public final String line2;
/**
@@ -94,9 +86,7 @@
@State
public final int state;
- @SystemApi
public final int flags;
- @SystemApi
public final int icon;
/**
@@ -108,7 +98,6 @@
this(id, summary, "", "", -1, state, FLAG_RELEVANT_ALWAYS);
}
- @SystemApi
public Condition(Uri id, String summary, String line1, String line2, int icon,
int state, int flags) {
if (id == null) throw new IllegalArgumentException("id is required");
@@ -177,7 +166,6 @@
proto.end(token);
}
- @SystemApi
public static String stateToString(int state) {
if (state == STATE_FALSE) return "STATE_FALSE";
if (state == STATE_TRUE) return "STATE_TRUE";
@@ -186,7 +174,6 @@
throw new IllegalArgumentException("state is invalid: " + state);
}
- @SystemApi
public static String relevanceToString(int flags) {
final boolean now = (flags & FLAG_RELEVANT_NOW) != 0;
final boolean always = (flags & FLAG_RELEVANT_ALWAYS) != 0;
@@ -219,7 +206,6 @@
return 0;
}
- @SystemApi
public Condition copy() {
final Parcel parcel = Parcel.obtain();
try {
@@ -231,14 +217,12 @@
}
}
- @SystemApi
public static Uri.Builder newId(Context context) {
return new Uri.Builder()
.scheme(Condition.SCHEME)
.authority(context.getPackageName());
}
- @SystemApi
public static boolean isValidId(Uri id, String pkg) {
return id != null && SCHEME.equals(id.getScheme()) && pkg.equals(id.getAuthority());
}
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 6fc689a..5203c8f 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -17,7 +17,6 @@
package android.service.notification;
import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.INotificationManager;
@@ -107,7 +106,6 @@
*/
abstract public void onConnected();
- @SystemApi
public void onRequestConditions(int relevance) {}
/**
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index d7359f1..3b813c7 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -81,7 +81,6 @@
* {@link android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE} permission so
* that other applications can not abuse it.
*/
- @SystemApi
public static final String SERVICE_INTERFACE =
"android.service.textclassifier.TextClassifierService";
@@ -407,9 +406,7 @@
* Callbacks for TextClassifierService results.
*
* @param <T> the type of the result
- * @hide
*/
- @SystemApi
public interface Callback<T> {
/**
* Returns the result.
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index f9370a8..7e41878 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -377,6 +377,9 @@
* @param start the inclusive start offset of the target region in the text
* @param end the exclusive end offset of the target region in the text
* @param textDir the text direction
+ * @param computeHyphenation true if need to compute hyphenation, otherwise false
+ * @param computeLayout true if need to compute full layout, otherwise false.
+ * @param hint pass if you already have measured paragraph.
* @param recycle pass existing MeasuredParagraph if you want to recycle it.
*
* @return measured text
@@ -389,12 +392,18 @@
@NonNull TextDirectionHeuristic textDir,
boolean computeHyphenation,
boolean computeLayout,
+ @Nullable MeasuredParagraph hint,
@Nullable MeasuredParagraph recycle) {
final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
mt.resetAndAnalyzeBidi(text, start, end, textDir);
- final MeasuredText.Builder builder = new MeasuredText.Builder(mt.mCopiedBuffer);
- builder.setComputeHyphenation(computeHyphenation);
- builder.setComputeLayout(computeLayout);
+ final MeasuredText.Builder builder;
+ if (hint == null) {
+ builder = new MeasuredText.Builder(mt.mCopiedBuffer)
+ .setComputeHyphenation(computeHyphenation)
+ .setComputeLayout(computeLayout);
+ } else {
+ builder = new MeasuredText.Builder(hint.mMeasuredText);
+ }
if (mt.mTextLength == 0) {
// Need to build empty native measured text for StaticLayout.
// TODO: Stop creating empty measured text for empty lines.
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index b7ea012..08741d6 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -17,6 +17,7 @@
package android.text;
import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,6 +26,8 @@
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
@@ -119,6 +122,16 @@
}
/**
+ * Builder constructor from existing params.
+ */
+ public Builder(@NonNull Params params) {
+ mPaint = params.mPaint;
+ mTextDir = params.mTextDir;
+ mBreakStrategy = params.mBreakStrategy;
+ mHyphenationFrequency = params.mHyphenationFrequency;
+ }
+
+ /**
* Set the line break strategy.
*
* The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
@@ -220,13 +233,41 @@
}
/** @hide */
- public boolean isSameTextMetricsInternal(@NonNull TextPaint paint,
+ @IntDef(value = { UNUSABLE, NEED_RECOMPUTE, USABLE })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CheckResultUsableResult {}
+
+ /**
+ * Constant for returning value of checkResultUsable indicating that given parameter is not
+ * compatible.
+ * @hide
+ */
+ public static final int UNUSABLE = 0;
+
+ /**
+ * Constant for returning value of checkResultUsable indicating that given parameter is not
+ * compatible but partially usable for creating new PrecomputedText.
+ * @hide
+ */
+ public static final int NEED_RECOMPUTE = 1;
+
+ /**
+ * Constant for returning value of checkResultUsable indicating that given parameter is
+ * compatible.
+ * @hide
+ */
+ public static final int USABLE = 2;
+
+ /** @hide */
+ public @CheckResultUsableResult int checkResultUsable(@NonNull TextPaint paint,
@NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy,
@Layout.HyphenationFrequency int frequency) {
- return mTextDir == textDir
- && mBreakStrategy == strategy
- && mHyphenationFrequency == frequency
- && mPaint.equalsForTextMeasurement(paint);
+ if (mBreakStrategy == strategy && mHyphenationFrequency == frequency
+ && mPaint.equalsForTextMeasurement(paint)) {
+ return mTextDir == textDir ? USABLE : NEED_RECOMPUTE;
+ } else {
+ return UNUSABLE;
+ }
}
/**
@@ -243,8 +284,8 @@
return false;
}
Params param = (Params) o;
- return isSameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy,
- param.mHyphenationFrequency);
+ return checkResultUsable(param.mPaint, param.mTextDir, param.mBreakStrategy,
+ param.mHyphenationFrequency) == Params.USABLE;
}
@Override
@@ -321,11 +362,55 @@
* @return A {@link PrecomputedText}
*/
public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) {
- ParagraphInfo[] paraInfo = createMeasuredParagraphs(
- text, params, 0, text.length(), true /* computeLayout */);
+ ParagraphInfo[] paraInfo = null;
+ if (text instanceof PrecomputedText) {
+ final PrecomputedText hintPct = (PrecomputedText) text;
+ final PrecomputedText.Params hintParams = hintPct.getParams();
+ final @Params.CheckResultUsableResult int checkResult =
+ hintParams.checkResultUsable(params.mPaint, params.mTextDir,
+ params.mBreakStrategy, params.mHyphenationFrequency);
+ switch (checkResult) {
+ case Params.USABLE:
+ return hintPct;
+ case Params.NEED_RECOMPUTE:
+ // To be able to use PrecomputedText for new params, at least break strategy and
+ // hyphenation frequency must be the same.
+ if (params.getBreakStrategy() == hintParams.getBreakStrategy()
+ && params.getHyphenationFrequency()
+ == hintParams.getHyphenationFrequency()) {
+ paraInfo = createMeasuredParagraphsFromPrecomputedText(
+ hintPct, params, true /* compute layout */);
+ }
+ break;
+ case Params.UNUSABLE:
+ // Unable to use anything in PrecomputedText. Create PrecomputedText as the
+ // normal text input.
+ }
+
+ }
+ if (paraInfo == null) {
+ paraInfo = createMeasuredParagraphs(
+ text, params, 0, text.length(), true /* computeLayout */);
+ }
return new PrecomputedText(text, 0, text.length(), params, paraInfo);
}
+ private static ParagraphInfo[] createMeasuredParagraphsFromPrecomputedText(
+ @NonNull PrecomputedText pct, @NonNull Params params, boolean computeLayout) {
+ final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
+ && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
+ ArrayList<ParagraphInfo> result = new ArrayList<>();
+ for (int i = 0; i < pct.getParagraphCount(); ++i) {
+ final int paraStart = pct.getParagraphStart(i);
+ final int paraEnd = pct.getParagraphEnd(i);
+ result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
+ params.getTextPaint(), pct, paraStart, paraEnd, params.getTextDirection(),
+ needHyphenation, computeLayout, pct.getMeasuredParagraph(i),
+ null /* no recycle */)));
+ }
+ return result.toArray(new ParagraphInfo[result.size()]);
+ }
+
/** @hide */
public static ParagraphInfo[] createMeasuredParagraphs(
@NonNull CharSequence text, @NonNull Params params,
@@ -350,7 +435,8 @@
result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(),
- needHyphenation, computeLayout, null /* no recycle */)));
+ needHyphenation, computeLayout, null /* no hint */,
+ null /* no recycle */)));
}
return result.toArray(new ParagraphInfo[result.size()]);
}
@@ -434,12 +520,15 @@
* Returns true if the given TextPaint gives the same result of text layout for this text.
* @hide
*/
- public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
- @NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint,
- @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
- return mStart == start
- && mEnd == end
- && mParams.isSameTextMetricsInternal(paint, textDir, strategy, frequency);
+ public @Params.CheckResultUsableResult int checkResultUsable(@IntRange(from = 0) int start,
+ @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir,
+ @NonNull TextPaint paint, @Layout.BreakStrategy int strategy,
+ @Layout.HyphenationFrequency int frequency) {
+ if (mStart != start || mEnd != end) {
+ return Params.UNUSABLE;
+ } else {
+ return mParams.checkResultUsable(paint, textDir, strategy, frequency);
+ }
}
/** @hide */
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 8cb18b2..3d0c662 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -650,10 +650,26 @@
final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null;
if (source instanceof PrecomputedText) {
PrecomputedText precomputed = (PrecomputedText) source;
- if (precomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint,
- b.mBreakStrategy, b.mHyphenationFrequency)) {
- // Some parameters are different from the ones when measured text is created.
- paragraphInfo = precomputed.getParagraphInfo();
+ final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
+ precomputed.checkResultUsable(bufStart, bufEnd, textDir, paint,
+ b.mBreakStrategy, b.mHyphenationFrequency);
+ switch (checkResult) {
+ case PrecomputedText.Params.UNUSABLE:
+ break;
+ case PrecomputedText.Params.NEED_RECOMPUTE:
+ final PrecomputedText.Params newParams =
+ new PrecomputedText.Params.Builder(paint)
+ .setBreakStrategy(b.mBreakStrategy)
+ .setHyphenationFrequency(b.mHyphenationFrequency)
+ .setTextDirection(textDir)
+ .build();
+ precomputed = PrecomputedText.create(precomputed, newParams);
+ paragraphInfo = precomputed.getParagraphInfo();
+ break;
+ case PrecomputedText.Params.USABLE:
+ // Some parameters are different from the ones when measured text is created.
+ paragraphInfo = precomputed.getParagraphInfo();
+ break;
}
}
diff --git a/core/java/android/view/InputEventCompatProcessor.java b/core/java/android/view/InputEventCompatProcessor.java
new file mode 100644
index 0000000..ff8407a
--- /dev/null
+++ b/core/java/android/view/InputEventCompatProcessor.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.content.Context;
+import android.os.Build;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Compatibility processor for InputEvents that allows events to be adjusted before and
+ * after it is sent to the application.
+ *
+ * {@hide}
+ */
+public class InputEventCompatProcessor {
+
+ protected Context mContext;
+ protected int mTargetSdkVersion;
+
+ /** List of events to be used to return the processed events */
+ private List<InputEvent> mProcessedEvents;
+
+ public InputEventCompatProcessor(Context context) {
+ mContext = context;
+ mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+ mProcessedEvents = new ArrayList<>();
+ }
+
+ /**
+ * Processes the InputEvent for compatibility before it is sent to the app, allowing for the
+ * generation of more than one event if necessary.
+ *
+ * @param e The InputEvent to process
+ * @return The list of adjusted events, or null if no adjustments are needed. Do not keep a
+ * reference to the output as the list is reused.
+ */
+ public List<InputEvent> processInputEventForCompatibility(InputEvent e) {
+ if (mTargetSdkVersion < Build.VERSION_CODES.M && e instanceof MotionEvent) {
+ mProcessedEvents.clear();
+ MotionEvent motion = (MotionEvent) e;
+ final int mask =
+ MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
+ final int buttonState = motion.getButtonState();
+ final int compatButtonState = (buttonState & mask) >> 4;
+ if (compatButtonState != 0) {
+ motion.setButtonState(buttonState | compatButtonState);
+ }
+ mProcessedEvents.add(motion);
+ return mProcessedEvents;
+ }
+ return null;
+ }
+
+ /**
+ * Processes the InputEvent for compatibility before it is finished by calling
+ * InputEventReceiver#finishInputEvent().
+ *
+ * @param e The InputEvent to process
+ * @return The InputEvent to finish, or null if it should not be finished
+ */
+ public InputEvent processInputEventBeforeFinish(InputEvent e) {
+ // No changes needed
+ return e;
+ }
+}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index ba5340c..fb4f9c0 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -38,9 +38,14 @@
private final InsetsState mState = new InsetsState();
private final Rect mFrame = new Rect();
private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
+ private final ViewRootImpl mViewRoot;
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
+ public InsetsController(ViewRootImpl viewRoot) {
+ mViewRoot = viewRoot;
+ }
+
void onFrameChanged(Rect frame) {
mFrame.set(frame);
}
@@ -49,8 +54,14 @@
return mState;
}
- public void setState(InsetsState state) {
+ boolean onStateChanged(InsetsState state) {
+ if (mState.equals(state)) {
+ return false;
+ }
mState.set(state);
+ applyLocalVisibilityOverride();
+ mViewRoot.notifyInsetsChanged();
+ return true;
}
/**
@@ -105,17 +116,28 @@
}
}
+ private void applyLocalVisibilityOverride() {
+ for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+ final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
+ controller.applyLocalVisibilityOverride();
+ }
+ }
+
@VisibleForTesting
public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetType int type) {
InsetsSourceConsumer controller = mSourceConsumers.get(type);
if (controller != null) {
return controller;
}
- controller = new InsetsSourceConsumer(type, mState, Transaction::new);
+ controller = new InsetsSourceConsumer(type, mState, Transaction::new, this);
mSourceConsumers.put(type, controller);
return controller;
}
+ void notifyVisibilityChanged() {
+ mViewRoot.notifyInsetsChanged();
+ }
+
void dump(String prefix, PrintWriter pw) {
pw.println(prefix); pw.println("InsetsController:");
mState.dump(prefix + " ", pw);
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index e74aa8d..ec85c4c 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -33,27 +33,31 @@
private final Supplier<Transaction> mTransactionSupplier;
private final @InternalInsetType int mType;
private final InsetsState mState;
- private @Nullable InsetsSourceControl mControl;
+ private final InsetsController mController;
+ private @Nullable InsetsSourceControl mSourceControl;
private boolean mHidden;
public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state,
- Supplier<Transaction> transactionSupplier) {
+ Supplier<Transaction> transactionSupplier, InsetsController controller) {
mType = type;
mState = state;
mTransactionSupplier = transactionSupplier;
+ mController = controller;
}
public void setControl(@Nullable InsetsSourceControl control) {
- if (mControl == control) {
+ if (mSourceControl == control) {
return;
}
- mControl = control;
+ mSourceControl = control;
applyHiddenToControl();
+ applyLocalVisibilityOverride();
+ mController.notifyVisibilityChanged();
}
@VisibleForTesting
public InsetsSourceControl getControl() {
- return mControl;
+ return mSourceControl;
}
int getType() {
@@ -70,25 +74,36 @@
setHidden(true);
}
+ void applyLocalVisibilityOverride() {
+
+ // If we don't have control, we are not able to change the visibility.
+ if (mSourceControl == null) {
+ return;
+ }
+ mState.getSource(mType).setVisible(!mHidden);
+ }
+
private void setHidden(boolean hidden) {
if (mHidden == hidden) {
return;
}
mHidden = hidden;
applyHiddenToControl();
+ applyLocalVisibilityOverride();
+ mController.notifyVisibilityChanged();
}
private void applyHiddenToControl() {
- if (mControl == null) {
+ if (mSourceControl == null) {
return;
}
// TODO: Animation
final Transaction t = mTransactionSupplier.get();
if (mHidden) {
- t.hide(mControl.getLeash());
+ t.hide(mSourceControl.getLeash());
} else {
- t.show(mControl.getLeash());
+ t.show(mSourceControl.getLeash());
}
t.apply();
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ab01085..a006e5d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -36,6 +36,8 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -129,6 +131,12 @@
int width, int height);
private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(
IBinder displayToken);
+ private static native DisplayedContentSamplingAttributes
+ nativeGetDisplayedContentSamplingAttributes(IBinder displayToken);
+ private static native boolean nativeSetDisplayedContentSamplingEnabled(IBinder displayToken,
+ boolean enable, int componentMask, int maxFrames);
+ private static native DisplayedContentSample nativeGetDisplayedContentSample(
+ IBinder displayToken, long numFrames, long timestamp);
private static native int nativeGetActiveConfig(IBinder displayToken);
private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
@@ -1164,6 +1172,45 @@
return nativeGetActiveConfig(displayToken);
}
+ /**
+ * @hide
+ */
+ public static DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributes(
+ IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ return nativeGetDisplayedContentSamplingAttributes(displayToken);
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean setDisplayedContentSamplingEnabled(
+ IBinder displayToken, boolean enable, int componentMask, int maxFrames) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ final int maxColorComponents = 4;
+ if ((componentMask >> maxColorComponents) != 0) {
+ throw new IllegalArgumentException("invalid componentMask when enabling sampling");
+ }
+ return nativeSetDisplayedContentSamplingEnabled(
+ displayToken, enable, componentMask, maxFrames);
+ }
+
+ /**
+ * @hide
+ */
+ public static DisplayedContentSample getDisplayedContentSample(
+ IBinder displayToken, long maxFrames, long timestamp) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ return nativeGetDisplayedContentSample(displayToken, maxFrames, timestamp);
+ }
+
+
public static boolean setActiveConfig(IBinder displayToken, int id) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4b9a2b9..9fe0ddc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -124,6 +124,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
+import java.util.List;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
@@ -462,7 +463,7 @@
final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
boolean mPendingAlwaysConsumeNavBar;
- private InsetsState mPendingInsets = new InsetsState();
+ private InsetsState mTempInsets = new InsetsState();
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
@@ -543,6 +544,8 @@
private boolean mNeedsRendererSetup;
+ private final InputEventCompatProcessor mInputCompatProcessor;
+
/**
* Consistency verifier for debugging purposes.
*/
@@ -550,7 +553,7 @@
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
- private final InsetsController mInsetsController = new InsetsController();
+ private final InsetsController mInsetsController = new InsetsController(this);
static final class SystemUiVisibilityInfo {
int seq;
@@ -598,6 +601,25 @@
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+ String processorOverrideName = context.getResources().getString(
+ R.string.config_inputEventCompatProcessorOverrideClassName);
+ if (processorOverrideName.isEmpty()) {
+ // No compatibility processor override, using default.
+ mInputCompatProcessor = new InputEventCompatProcessor(context);
+ } else {
+ InputEventCompatProcessor compatProcessor = null;
+ try {
+ final Class<? extends InputEventCompatProcessor> klass =
+ (Class<? extends InputEventCompatProcessor>) Class.forName(
+ processorOverrideName);
+ compatProcessor = klass.getConstructor(Context.class).newInstance(context);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to create the InputEventCompatProcessor. ", e);
+ } finally {
+ mInputCompatProcessor = compatProcessor;
+ }
+ }
+
if (!sCompatibilityDone) {
sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
@@ -821,7 +843,7 @@
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
- mInsetsController.getState());
+ mTempInsets);
setFrame(mTmpFrame);
} catch (RemoteException e) {
mAdded = false;
@@ -849,7 +871,7 @@
mAttachInfo.mAlwaysConsumeNavBar =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
- mPendingInsets = mInsetsController.getState();
+ mInsetsController.onStateChanged(mTempInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
@@ -1342,6 +1364,19 @@
scheduleTraversals();
}
+ void notifyInsetsChanged() {
+ if (!USE_NEW_INSETS) {
+ return;
+ }
+ mApplyInsetsRequested = true;
+
+ // If this changes during traversal, no need to schedule another one as it will dispatch it
+ // during the current traversal.
+ if (!mIsInTraversal) {
+ scheduleTraversals();
+ }
+ }
+
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
@@ -2027,9 +2062,6 @@
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;
@@ -2223,8 +2255,6 @@
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;
@@ -2262,10 +2292,6 @@
mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
contentInsetsChanged = true;
}
- if (insetsStateChanged) {
- mInsetsController.setState(mPendingInsets);
- contentInsetsChanged = true;
- }
if (contentInsetsChanged || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
|| mLastOverscanRequested != mAttachInfo.mOverscanRequested
@@ -4375,22 +4401,12 @@
}
break;
case MSG_INSETS_CHANGED:
- mPendingInsets = (InsetsState) msg.obj;
-
- // TODO: Full traversal not needed here.
- if (USE_NEW_INSETS) {
- requestLayout();
- }
+ mInsetsController.onStateChanged((InsetsState) msg.obj);
break;
case MSG_INSETS_CONTROL_CHANGED: {
SomeArgs args = (SomeArgs) msg.obj;
- mPendingInsets = (InsetsState) args.arg1;
mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
-
- // TODO: Full traversal not necessarily needed here.
- if (USE_NEW_INSETS) {
- requestLayout();
- }
+ mInsetsController.onStateChanged((InsetsState) args.arg1);
break;
}
case MSG_WINDOW_MOVED:
@@ -6791,7 +6807,7 @@
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
- mPendingMergedConfiguration, mSurface, mPendingInsets);
+ mPendingMergedConfiguration, mSurface, mTempInsets);
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -6808,7 +6824,7 @@
mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
}
setFrame(mTmpFrame);
-
+ mInsetsController.onStateChanged(mTempInsets);
return relayoutResult;
}
@@ -7172,6 +7188,7 @@
public static final int FLAG_FINISHED_HANDLED = 1 << 3;
public static final int FLAG_RESYNTHESIZED = 1 << 4;
public static final int FLAG_UNHANDLED = 1 << 5;
+ public static final int FLAG_MODIFIED_FOR_COMPATIBILITY = 1 << 6;
public QueuedInputEvent mNext;
@@ -7264,7 +7281,6 @@
@UnsupportedAppUsage
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
- adjustInputEventForCompatibility(event);
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// Always enqueue the input event in order, regardless of its time stamp.
@@ -7367,7 +7383,22 @@
if (q.mReceiver != null) {
boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
- q.mReceiver.finishInputEvent(q.mEvent, handled);
+ boolean modified = (q.mFlags & QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY) != 0;
+ if (modified) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventBeforeFinish");
+ InputEvent processedEvent;
+ try {
+ processedEvent =
+ mInputCompatProcessor.processInputEventBeforeFinish(q.mEvent);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ if (processedEvent != null) {
+ q.mReceiver.finishInputEvent(processedEvent, handled);
+ }
+ } else {
+ q.mReceiver.finishInputEvent(q.mEvent, handled);
+ }
} else {
q.mEvent.recycleIfNeededAfterDispatch();
}
@@ -7375,19 +7406,6 @@
recycleQueuedInputEvent(q);
}
- private void adjustInputEventForCompatibility(InputEvent e) {
- if (mTargetSdkVersion < Build.VERSION_CODES.M && e instanceof MotionEvent) {
- MotionEvent motion = (MotionEvent) e;
- final int mask =
- MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
- final int buttonState = motion.getButtonState();
- final int compatButtonState = (buttonState & mask) >> 4;
- if (compatButtonState != 0) {
- motion.setButtonState(buttonState | compatButtonState);
- }
- }
- }
-
static boolean isTerminalInputEvent(InputEvent event) {
if (event instanceof KeyEvent) {
final KeyEvent keyEvent = (KeyEvent)event;
@@ -7458,7 +7476,28 @@
@Override
public void onInputEvent(InputEvent event) {
- enqueueInputEvent(event, this, 0, true);
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
+ List<InputEvent> processedEvents;
+ try {
+ processedEvents =
+ mInputCompatProcessor.processInputEventForCompatibility(event);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ if (processedEvents != null) {
+ if (processedEvents.isEmpty()) {
+ // InputEvent consumed by mInputCompatProcessor
+ finishInputEvent(event, true);
+ } else {
+ for (int i = 0; i < processedEvents.size(); i++) {
+ enqueueInputEvent(
+ processedEvents.get(i), this,
+ QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
+ }
+ }
+ } else {
+ enqueueInputEvent(event, this, 0, true);
+ }
}
@Override
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index f7c9a0b..763ce4f 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Rect;
@@ -749,7 +748,6 @@
*
* @param callback The callback to invoke when the frame is committed.
*/
- @TestApi
public void registerFrameCommitCallback(@NonNull Runnable callback) {
checkIsAlive();
if (mOnFrameCommitListeners == null) {
@@ -772,7 +770,6 @@
* not be invoked. If false is returned then the callback was either never added
* or may already be pending execution and was unable to be removed
*/
- @TestApi
public boolean unregisterFrameCommitCallback(@NonNull Runnable callback) {
checkIsAlive();
if (mOnFrameCommitListeners == null) {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 88b9c80..c5c1bca 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1004,6 +1004,36 @@
}
/**
+ * Returns accessibility window id from window token. Accessibility window id is the one
+ * returned from AccessibilityWindowInfo.getId(). Only available for the system process.
+ *
+ * @param windowToken Window token to find accessibility window id.
+ * @return Accessibility window id for the window token.
+ * AccessibilityWindowInfo.UNDEFINED_WINDOW_ID if accessibility window id not available for
+ * the token.
+ * @hide
+ */
+ @SystemApi
+ public int getAccessibilityWindowId(IBinder windowToken) {
+ if (windowToken == null) {
+ return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ }
+
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ }
+ }
+ try {
+ return service.getAccessibilityWindowId(windowToken);
+ } catch (RemoteException e) {
+ return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ }
+ }
+
+ /**
* Sets the current state and notifies listeners, if necessary.
*
* @param stateFlags The state flags.
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 2767a82..38dac94 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -76,5 +76,8 @@
// System process only
boolean sendFingerprintGesture(int gestureKeyCode);
+ // System process only
+ int getAccessibilityWindowId(IBinder windowToken);
+
long getRecommendedTimeoutMillis();
}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index 797b861..b41096c 100644
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -16,7 +16,6 @@
package android.view.textclassifier;
-import android.annotation.NonNull;
import android.app.Person;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -30,6 +29,7 @@
import java.util.Deque;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -57,9 +57,9 @@
* </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> messages,
+ Function<CharSequence, String> languageDetector) {
List<ConversationActions.Message> messagesWithText =
messages.stream()
.filter(message -> !TextUtils.isEmpty(message.getText()))
@@ -67,31 +67,18 @@
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(),
- 0,
- null)};
- }
-
- // Encode the messages in the reverse order, stop whenever the Person object is missing.
Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
PersonEncoder personEncoder = new PersonEncoder();
+ int size = messagesWithText.size();
for (int i = size - 1; i >= 0; i--) {
ConversationActions.Message message = messagesWithText.get(i);
- if (message.getAuthor() == null) {
- break;
- }
+ long referenceTime = message.getReferenceTime() == null
+ ? 0
+ : message.getReferenceTime().toInstant().toEpochMilli();
nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
personEncoder.encode(message.getAuthor()),
- message.getText().toString(), 0, null));
+ message.getText().toString(), referenceTime,
+ languageDetector.apply(message.getText())));
}
return nativeMessages.toArray(
new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 04b94b0..04924c9 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -349,17 +349,31 @@
/**
* Represents the local user.
*
- * @see Builder#setAuthor(Person)
+ * @see Builder#Builder(Person)
*/
public static final Person PERSON_USER_LOCAL =
new Person.Builder()
.setKey("text-classifier-conversation-actions-local-user")
.build();
+ /**
+ * Represents the remote user.
+ * <p>
+ * If possible, you are suggested to create a {@link Person} object that can identify
+ * the remote user better, so that the underlying model could differentiate between
+ * different remote users.
+ *
+ * @see Builder#Builder(Person)
+ */
+ public static final Person PERSON_USER_REMOTE =
+ new Person.Builder()
+ .setKey("text-classifier-conversation-actions-remote-user")
+ .build();
+
@Nullable
private final Person mAuthor;
@Nullable
- private final ZonedDateTime mComposeTime;
+ private final ZonedDateTime mReferenceTime;
@Nullable
private final CharSequence mText;
@NonNull
@@ -367,18 +381,18 @@
private Message(
@Nullable Person author,
- @Nullable ZonedDateTime composeTime,
+ @Nullable ZonedDateTime referenceTime,
@Nullable CharSequence text,
@NonNull Bundle bundle) {
mAuthor = author;
- mComposeTime = composeTime;
+ mReferenceTime = referenceTime;
mText = text;
mExtras = Preconditions.checkNotNull(bundle);
}
private Message(Parcel in) {
mAuthor = in.readParcelable(null);
- mComposeTime =
+ mReferenceTime =
in.readInt() == 0
? null
: ZonedDateTime.parse(
@@ -390,9 +404,9 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mAuthor, flags);
- parcel.writeInt(mComposeTime != null ? 1 : 0);
- if (mComposeTime != null) {
- parcel.writeString(mComposeTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+ parcel.writeInt(mReferenceTime != null ? 1 : 0);
+ if (mReferenceTime != null) {
+ parcel.writeString(mReferenceTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
parcel.writeCharSequence(mText);
parcel.writeBundle(mExtras);
@@ -417,15 +431,18 @@
};
/** Returns the person that composed the message. */
- @Nullable
+ @NonNull
public Person getAuthor() {
return mAuthor;
}
- /** Returns the compose time of the message. */
+ /**
+ * Returns the reference time of the message, for example it could be the compose or send
+ * time of this message.
+ */
@Nullable
- public ZonedDateTime getTime() {
- return mComposeTime;
+ public ZonedDateTime getReferenceTime() {
+ return mReferenceTime;
}
/** Returns the text of the message. */
@@ -451,34 +468,38 @@
@Nullable
private Person mAuthor;
@Nullable
- private ZonedDateTime mComposeTime;
+ private ZonedDateTime mReferenceTime;
@Nullable
private CharSequence mText;
@Nullable
private Bundle mExtras;
/**
- * Sets the person who composed this message.
- * <p>
- * Use {@link #PERSON_USER_LOCAL} to represent the local user.
+ * Constructs a builder.
+ *
+ * @param author the person that composed the message, use {@link #PERSON_USER_LOCAL}
+ * to represent the local user. If it is not possible to identify the
+ * remote user that the local user is conversing with, use
+ * {@link #PERSON_USER_REMOTE} to represent a remote user.
*/
- @NonNull
- public Builder setAuthor(@Nullable Person author) {
- mAuthor = author;
- return this;
+ public Builder(@NonNull Person author) {
+ mAuthor = Preconditions.checkNotNull(author);
}
- /** Sets the text of this message */
+ /** Sets the text of this message. */
@NonNull
public Builder setText(@Nullable CharSequence text) {
mText = text;
return this;
}
- /** Sets the compose time of this message */
+ /**
+ * Sets the reference time of this message, for example it could be the compose or send
+ * time of this message.
+ */
@NonNull
- public Builder setComposeTime(@Nullable ZonedDateTime composeTime) {
- mComposeTime = composeTime;
+ public Builder setReferenceTime(@Nullable ZonedDateTime referenceTime) {
+ mReferenceTime = referenceTime;
return this;
}
@@ -494,7 +515,7 @@
public Message build() {
return new Message(
mAuthor,
- mComposeTime,
+ mReferenceTime,
mText == null ? null : new SpannedString(mText),
mExtras == null ? new Bundle() : mExtras.deepCopy());
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index a2536cb..ea82bf3 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -162,6 +162,14 @@
TextClassifier NO_OP = new TextClassifier() {};
/**
+ * Used as a boolean value to indicate the intent is generated by TextClassifier.
+ * <p>
+ * All {@link TextClassifier} implementations should set this boolean extra to be true in their
+ * generated intents.
+ */
+ String EXTRA_FROM_TEXT_CLASSIFIER = "android.view.textclassifier.extra.FROM_TEXT_CLASSIFIER";
+
+ /**
* Returns suggested text selection start and end indices, recognized entity types, and their
* associated confidence scores. The entity types are ordered from highest to lowest scoring.
*
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 8e14dfd..deda926 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -139,7 +139,7 @@
FACTORY_MODEL_DIR,
LANG_ID_FACTORY_MODEL_FILENAME_REGEX,
UPDATED_LANG_ID_MODEL_FILE,
- fd -> -1, // TODO: Replace this with LangIdModel.getVersion(fd)
+ LangIdModel::getVersion,
fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT));
mActionsModelFileManager = new ModelFileManager(
new ModelFileManager.ModelFileSupplierImpl(
@@ -374,7 +374,8 @@
return mFallback.suggestConversationActions(request);
}
ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
- ActionsSuggestionsHelper.toNativeMessages(request.getConversation());
+ ActionsSuggestionsHelper.toNativeMessages(request.getConversation(),
+ this::detectLanguageTagsFromText);
if (nativeMessages.length == 0) {
return mFallback.suggestConversationActions(request);
}
@@ -407,6 +408,26 @@
return mFallback.suggestConversationActions(request);
}
+ @Nullable
+ private String detectLanguageTagsFromText(CharSequence text) {
+ TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
+ TextLanguage textLanguage = detectLanguage(request);
+ int localeHypothesisCount = textLanguage.getLocaleHypothesisCount();
+ List<String> languageTags = new ArrayList<>();
+ // TODO: Reconsider this and probably make the score threshold configurable.
+ for (int i = 0; i < localeHypothesisCount; i++) {
+ ULocale locale = textLanguage.getLocale(i);
+ if (textLanguage.getConfidenceScore(locale) < 0.5) {
+ break;
+ }
+ languageTags.add(locale.toLanguageTag());
+ }
+ if (languageTags.isEmpty()) {
+ return LocaleList.getDefault().toLanguageTags();
+ }
+ return String.join(",", languageTags);
+ }
+
private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
List<String> defaultActionTypes =
request.getHints().contains(ConversationActions.HINT_FOR_NOTIFICATION)
@@ -777,6 +798,9 @@
if (foreignText) {
insertTranslateAction(actions, context, text);
}
+ actions.forEach(
+ action -> action.getIntent()
+ .putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true));
return actions;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2a42232..90da812 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6028,14 +6028,22 @@
if (mTextDir == null) {
mTextDir = getTextDirectionHeuristic();
}
- if (!precomputed.getParams().isSameTextMetricsInternal(
- getPaint(), mTextDir, mBreakStrategy, mHyphenationFrequency)) {
- throw new IllegalArgumentException(
+ final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
+ precomputed.getParams().checkResultUsable(getPaint(), mTextDir, mBreakStrategy,
+ mHyphenationFrequency);
+ switch (checkResult) {
+ case PrecomputedText.Params.UNUSABLE:
+ throw new IllegalArgumentException(
"PrecomputedText's Parameters don't match the parameters of this TextView."
+ "Consider using setTextMetricsParams(precomputedText.getParams()) "
+ "to override the settings of this TextView: "
+ "PrecomputedText: " + precomputed.getParams()
+ "TextView: " + getTextMetricsParams());
+ case PrecomputedText.Params.NEED_RECOMPUTE:
+ precomputed = PrecomputedText.create(precomputed, getTextMetricsParams());
+ break;
+ case PrecomputedText.Params.USABLE:
+ // pass through
}
} else if (type == BufferType.SPANNABLE || mMovement != null) {
text = mSpannableFactory.newSpannable(text);
@@ -10209,7 +10217,7 @@
}
// ContentCapture
- if (isImportantForContentCapture() && isTextEditable()) {
+ if (isLaidOut() && isImportantForContentCapture() && isTextEditable()) {
final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
if (cm != null && cm.isContentCaptureEnabled()) {
// TODO(b/111276913): pass flags when edited by user / add CTS test
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 8bc90a8..a27dbea 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -39,6 +39,7 @@
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
import android.provider.MetadataReader;
+import android.system.Int64Ref;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -53,6 +54,12 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -122,18 +129,56 @@
throw new FileNotFoundException("Can't find the file for documentId: " + documentId);
}
+ final String mimeType = getDocumentType(documentId);
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ final Int64Ref treeCount = new Int64Ref(0);
+ final Int64Ref treeSize = new Int64Ref(0);
+ try {
+ final Path path = FileSystems.getDefault().getPath(file.getAbsolutePath());
+ Files.walkFileTree(path, new FileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ treeCount.value += 1;
+ treeSize.value += attrs.size();
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFileFailed(Path file, IOException exc) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException e) {
+ Log.e(TAG, "An error occurred retrieving the metadata", e);
+ return null;
+ }
+
+ final Bundle res = new Bundle();
+ res.putLong(DocumentsContract.METADATA_TREE_COUNT, treeCount.value);
+ res.putLong(DocumentsContract.METADATA_TREE_SIZE, treeSize.value);
+ return res;
+ }
+
if (!file.isFile()) {
Log.w(TAG, "Can't stream non-regular file. Returning empty metadata.");
return null;
}
-
if (!file.canRead()) {
Log.w(TAG, "Can't stream non-readable file. Returning empty metadata.");
return null;
}
-
- String mimeType = getDocumentType(documentId);
if (!MetadataReader.isSupportedMimeType(mimeType)) {
+ Log.w(TAG, "Unsupported type " + mimeType + ". Returning empty metadata.");
return null;
}
@@ -562,7 +607,8 @@
}
protected boolean typeSupportsMetadata(String mimeType) {
- return MetadataReader.isSupportedMimeType(mimeType);
+ return MetadataReader.isSupportedMimeType(mimeType)
+ || Document.MIME_TYPE_DIR.equals(mimeType);
}
protected final File getFileForDocId(String docId) throws FileNotFoundException {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index b00e6fd..2e674a5 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;
@@ -68,6 +69,9 @@
private static final int ALLOW_HIDDENAPI_WHITELISTING = 0x40;
private static final int ALLOW_ALL = ~0;
+ // property for runtime configuration differentiation
+ private static final String SKU_PROPERTY = "ro.boot.product.hardware.sku";
+
// Group-ids that are given to all packages as read from etc/permissions/*.xml.
int[] mGlobalGids;
@@ -344,6 +348,17 @@
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
+ String skuProperty = SystemProperties.get(SKU_PROPERTY, "");
+ if (!skuProperty.isEmpty()) {
+ String skuDir = "sku_" + skuProperty;
+
+ readPermissions(Environment.buildPath(
+ Environment.getOdmDirectory(), "etc", "sysconfig", skuDir), odmPermissionFlag);
+ readPermissions(Environment.buildPath(
+ Environment.getOdmDirectory(), "etc", "permissions", skuDir),
+ odmPermissionFlag);
+ }
+
// Allow OEM to customize features and OEM permissions
int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS;
readPermissions(Environment.buildPath(
@@ -380,6 +395,10 @@
// Iterate over the files in the directory and scan .xml files
File platformFile = null;
for (File f : libraryDir.listFiles()) {
+ if (!f.isFile()) {
+ continue;
+ }
+
// We'll read platform.xml last
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
platformFile = f;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 31bb1d5..43f8d00 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -132,7 +132,6 @@
"android/graphics/GIFMovie.cpp",
"android/graphics/GraphicBuffer.cpp",
"android/graphics/Graphics.cpp",
- "android/graphics/HarfBuzzNGFaceSkia.cpp",
"android/graphics/ImageDecoder.cpp",
"android/graphics/Interpolator.cpp",
"android/graphics/MaskFilter.cpp",
@@ -162,6 +161,7 @@
"android/graphics/pdf/PdfUtils.cpp",
"android/graphics/text/LineBreaker.cpp",
"android/graphics/text/MeasuredText.cpp",
+ "android_media_AudioEffectDescriptor.cpp",
"android_media_AudioRecord.cpp",
"android_media_AudioSystem.cpp",
"android_media_AudioTrack.cpp",
@@ -264,6 +264,7 @@
"libEGL",
"libGLESv1_CM",
"libGLESv2",
+ "libGLESv3",
"libvulkan",
"libziparchive",
"libETC1",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f9879cc..687b105 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -103,6 +103,7 @@
extern int register_android_hardware_UsbRequest(JNIEnv *env);
extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env);
+extern int register_android_media_AudioEffectDescriptor(JNIEnv *env);
extern int register_android_media_AudioRecord(JNIEnv *env);
extern int register_android_media_AudioSystem(JNIEnv *env);
extern int register_android_media_AudioTrack(JNIEnv *env);
@@ -1456,6 +1457,7 @@
REG_JNI(register_android_hardware_UsbDeviceConnection),
REG_JNI(register_android_hardware_UsbRequest),
REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
+ REG_JNI(register_android_media_AudioEffectDescriptor),
REG_JNI(register_android_media_AudioSystem),
REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioTrack),
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
deleted file mode 100644
index cfe742d..0000000
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (c) 2012 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define LOG_TAG "TextLayoutCache"
-
-#include "HarfBuzzNGFaceSkia.h"
-
-#include <stdlib.h>
-
-#include <log/log.h>
-
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <SkPoint.h>
-#include <SkRect.h>
-#include <SkTypeface.h>
-
-#include <hb.h>
-
-namespace android {
-
-static const bool kDebugGlyphs = false;
-
-// Our implementation of the callbacks which Harfbuzz requires by using Skia
-// calls. See the Harfbuzz source for references about what these callbacks do.
-
-struct HarfBuzzFontData {
- explicit HarfBuzzFontData(SkPaint* paint) : m_paint(paint) { }
- SkPaint* m_paint;
-};
-
-static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents)
-{
- ALOG_ASSERT(codepoint <= 0xFFFF);
- paint->setTextEncoding(kGlyphID_SkTextEncoding);
-
- SkScalar skWidth;
- SkRect skBounds;
- uint16_t glyph = codepoint;
-
- paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds);
- if (kDebugGlyphs) {
- ALOGD("returned glyph for %i: width = %f", codepoint, skWidth);
- }
- if (width)
- *width = SkScalarToHBFixed(skWidth);
- if (extents) {
- // Invert y-axis because Skia is y-grows-down but we set up harfbuzz to be y-grows-up.
- extents->x_bearing = SkScalarToHBFixed(skBounds.fLeft);
- extents->y_bearing = SkScalarToHBFixed(-skBounds.fTop);
- extents->width = SkScalarToHBFixed(skBounds.width());
- extents->height = SkScalarToHBFixed(-skBounds.height());
- }
-}
-
-static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData)
-{
- HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
- SkPaint* paint = hbFontData->m_paint;
- paint->setTextEncoding(kUTF32_SkTextEncoding);
-
- if (unicode > 0x10ffff) {
- unicode = 0xfffd;
- }
- SkUnichar unichar = unicode;
-
- uint16_t glyph16;
- paint->textToGlyphs(&unichar, sizeof(unichar), &glyph16);
- *glyph = glyph16;
- return !!*glyph;
-}
-
-static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData)
-{
- HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
- hb_position_t advance = 0;
-
- SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, &advance, 0);
- return advance;
-}
-
-static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData)
-{
- // Just return true, following the way that Harfbuzz-FreeType
- // implementation does.
- return true;
-}
-
-static hb_bool_t harfbuzzGetGlyphExtents(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* userData)
-{
- HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
-
- SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, 0, extents);
- return true;
-}
-
-static hb_font_funcs_t* harfbuzzSkiaGetFontFuncs()
-{
- static hb_font_funcs_t* harfbuzzSkiaFontFuncs = 0;
-
- // We don't set callback functions which we can't support.
- // Harfbuzz will use the fallback implementation if they aren't set.
- if (!harfbuzzSkiaFontFuncs) {
- harfbuzzSkiaFontFuncs = hb_font_funcs_create();
- hb_font_funcs_set_glyph_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyph, 0, 0);
- hb_font_funcs_set_glyph_h_advance_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
- hb_font_funcs_set_glyph_h_origin_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
- hb_font_funcs_set_glyph_extents_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphExtents, 0, 0);
- hb_font_funcs_make_immutable(harfbuzzSkiaFontFuncs);
- }
- return harfbuzzSkiaFontFuncs;
-}
-
-hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData)
-{
- SkTypeface* typeface = reinterpret_cast<SkTypeface*>(userData);
-
- const size_t tableSize = typeface->getTableSize(tag);
- if (!tableSize)
- return 0;
-
- char* buffer = reinterpret_cast<char*>(malloc(tableSize));
- if (!buffer)
- return 0;
- size_t actualSize = typeface->getTableData(tag, 0, tableSize, buffer);
- if (tableSize != actualSize) {
- free(buffer);
- return 0;
- }
-
- return hb_blob_create(const_cast<char*>(buffer), tableSize,
- HB_MEMORY_MODE_WRITABLE, buffer, free);
-}
-
-static void destroyHarfBuzzFontData(void* data) {
- delete (HarfBuzzFontData*)data;
-}
-
-hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY) {
- hb_font_t* font = hb_font_create(face);
-
- // Note: this needs to be reworked when we do subpixels
- int x_ppem = floor(sizeX + 0.5);
- int y_ppem = floor(sizeY + 0.5);
- hb_font_set_ppem(font, x_ppem, y_ppem);
- hb_font_set_scale(font, HBFloatToFixed(sizeX), HBFloatToFixed(sizeY));
-
- HarfBuzzFontData* data = new HarfBuzzFontData(paint);
- hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), data, destroyHarfBuzzFontData);
-
- return font;
-}
-
-} // namespace android
diff --git a/core/jni/android/graphics/text/MeasuredText.cpp b/core/jni/android/graphics/text/MeasuredText.cpp
index 0bfadb4..d7d96fb 100644
--- a/core/jni/android/graphics/text/MeasuredText.cpp
+++ b/core/jni/android/graphics/text/MeasuredText.cpp
@@ -84,14 +84,14 @@
// Regular JNI
static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr,
- jcharArray javaText, jboolean computeHyphenation,
- jboolean computeLayout) {
+ jlong hintPtr, jcharArray javaText, jboolean computeHyphenation,
+ jboolean computeLayout) {
ScopedCharArrayRO text(env, javaText);
const minikin::U16StringPiece textBuffer(text.get(), text.size());
// Pass the ownership to Java.
- return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation,
- computeLayout).release());
+ return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation, computeLayout,
+ toMeasuredParagraph(hintPtr)).release());
}
// Regular JNI
@@ -147,7 +147,7 @@
{"nInitBuilder", "()J", (void*) nInitBuilder},
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
- {"nBuildMeasuredText", "(J[CZZ)J", (void*) nBuildMeasuredText},
+ {"nBuildMeasuredText", "(JJ[CZZ)J", (void*) nBuildMeasuredText},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
};
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 98bc735..8a28034 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -225,7 +225,8 @@
gAudioFormatCstor,
audioFormatFromNative(event->audio_config.format),
event->audio_config.sample_rate,
- inChannelMaskFromNative(event->audio_config.channel_mask));
+ inChannelMaskFromNative(event->audio_config.channel_mask),
+ (jint)0 /* channelIndexMask */);
}
if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) {
diff --git a/core/jni/android_media_AudioEffectDescriptor.cpp b/core/jni/android_media_AudioEffectDescriptor.cpp
new file mode 100644
index 0000000..5175a05
--- /dev/null
+++ b/core/jni/android_media_AudioEffectDescriptor.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core_jni_helpers.h"
+#include "android_media_AudioErrors.h"
+#include "media/AudioEffect.h"
+
+using namespace android;
+
+static jclass gAudioEffectDescriptorClass;
+static jmethodID gAudioEffectDescriptorCstor;
+
+namespace android {
+
+jclass audioEffectDescriptorClass() {
+ return gAudioEffectDescriptorClass;
+}
+
+jint convertAudioEffectDescriptorFromNative(JNIEnv* env, jobject* jDescriptor,
+ const effect_descriptor_t* nDescriptor)
+{
+ jstring jType;
+ jstring jUuid;
+ jstring jConnect;
+ jstring jName;
+ jstring jImplementor;
+ char str[EFFECT_STRING_LEN_MAX];
+
+ if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
+ == EFFECT_FLAG_TYPE_AUXILIARY) {
+ jConnect = env->NewStringUTF("Auxiliary");
+ } else if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
+ == EFFECT_FLAG_TYPE_INSERT) {
+ jConnect = env->NewStringUTF("Insert");
+ } else if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
+ == EFFECT_FLAG_TYPE_PRE_PROC) {
+ jConnect = env->NewStringUTF("Pre Processing");
+ } else {
+ return (jint) AUDIO_JAVA_BAD_VALUE;
+ }
+
+ AudioEffect::guidToString(&nDescriptor->type, str, EFFECT_STRING_LEN_MAX);
+ jType = env->NewStringUTF(str);
+
+ AudioEffect::guidToString(&nDescriptor->uuid, str, EFFECT_STRING_LEN_MAX);
+ jUuid = env->NewStringUTF(str);
+
+ jName = env->NewStringUTF(nDescriptor->name);
+ jImplementor = env->NewStringUTF(nDescriptor->implementor);
+
+ *jDescriptor = env->NewObject(gAudioEffectDescriptorClass,
+ gAudioEffectDescriptorCstor,
+ jType,
+ jUuid,
+ jConnect,
+ jName,
+ jImplementor);
+ env->DeleteLocalRef(jType);
+ env->DeleteLocalRef(jUuid);
+ env->DeleteLocalRef(jConnect);
+ env->DeleteLocalRef(jName);
+ env->DeleteLocalRef(jImplementor);
+
+ return (jint) AUDIO_JAVA_SUCCESS;
+}
+
+void convertAudioEffectDescriptorVectorFromNative(JNIEnv *env, jobjectArray *jDescriptors,
+ const std::vector<effect_descriptor_t>& nDescriptors)
+{
+ jobjectArray temp = env->NewObjectArray(nDescriptors.size(),
+ audioEffectDescriptorClass(), NULL);
+ size_t actualSize = 0;
+ for (size_t i = 0; i < nDescriptors.size(); i++) {
+ jobject jdesc;
+ if (convertAudioEffectDescriptorFromNative(env,
+ &jdesc,
+ &nDescriptors[i])
+ != AUDIO_JAVA_SUCCESS) {
+ continue;
+ }
+
+ env->SetObjectArrayElement(temp, actualSize++, jdesc);
+ env->DeleteLocalRef(jdesc);
+ }
+
+ *jDescriptors = env->NewObjectArray(actualSize, audioEffectDescriptorClass(), NULL);
+ for (size_t i = 0; i < actualSize; i++) {
+ env->SetObjectArrayElement(*jDescriptors,
+ i,
+ env->GetObjectArrayElement(temp, i));
+ }
+ env->DeleteLocalRef(temp);
+}
+
+}; // namespace android
+
+int register_android_media_AudioEffectDescriptor(JNIEnv* env) {
+ jclass audioEffectDescriptorClass =
+ FindClassOrDie(env, "android/media/audiofx/AudioEffect$Descriptor");
+ gAudioEffectDescriptorClass =
+ MakeGlobalRefOrDie(env, audioEffectDescriptorClass);
+ gAudioEffectDescriptorCstor =
+ GetMethodIDOrDie(env,
+ audioEffectDescriptorClass,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+
+ env->DeleteLocalRef(audioEffectDescriptorClass);
+ return 0;
+}
diff --git a/core/jni/android_media_AudioEffectDescriptor.h b/core/jni/android_media_AudioEffectDescriptor.h
new file mode 100644
index 0000000..d07188c
--- /dev/null
+++ b/core/jni/android_media_AudioEffectDescriptor.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_AUDIOEFFECT_DESCRIPTOR_H
+#define ANDROID_MEDIA_AUDIOEFFECT_DESCRIPTOR_H
+
+#include <system/audio.h>
+#include <system/audio_effect.h>
+
+#include "jni.h"
+
+namespace android {
+
+// Conversion from C effect_descriptor_t to Java AudioEffect.Descriptor object
+
+extern jclass audioEffectDescriptorClass();
+
+extern jint convertAudioEffectDescriptorFromNative(JNIEnv *env, jobject *jDescriptor,
+ const effect_descriptor_t *nDescriptor);
+
+extern void convertAudioEffectDescriptorVectorFromNative(JNIEnv *env, jobjectArray *jDescriptors,
+ const std::vector<effect_descriptor_t>& nDescriptors);
+} // namespace android
+
+#endif
\ No newline at end of file
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index adab8e2..283eb03 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2061,6 +2061,12 @@
return (jint)nativeToJavaStatus(status);
}
+static jboolean
+android_media_AudioSystem_isHapticPlaybackSupported(JNIEnv *env, jobject thiz)
+{
+ return AudioSystem::isHapticPlaybackSupported();
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -2123,6 +2129,7 @@
{"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
{"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
{"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
+ {"isHapticPlaybackSupported", "()Z", (void *)android_media_AudioSystem_isHapticPlaybackSupported},
};
static const JNINativeMethod gEventHandlerMethods[] = {
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 377e65c..102a0b7 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1137,7 +1137,7 @@
UniqueFile file = MakeUniqueFile(status_path.c_str(), "re");
char line[256];
- while (fgets(line, sizeof(line), file.get())) {
+ while (file != nullptr && fgets(line, sizeof(line), file.get())) {
jlong v;
if ( sscanf(line, "VmRSS: %" SCNd64 " kB", &v) == 1) {
rss[0] = v;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ea6e017..c745c16 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -37,6 +37,7 @@
#include <stdio.h>
#include <system/graphics.h>
#include <ui/DisplayInfo.h>
+#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
@@ -97,6 +98,16 @@
jmethodID builder;
} gGraphicBufferClassInfo;
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gDisplayedContentSampleClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gDisplayedContentSamplingAttributesClassInfo;
+
// ----------------------------------------------------------------------------
static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
@@ -398,6 +409,73 @@
return javaObjectForIBinder(env, token);
}
+static jobject nativeGetDisplayedContentSamplingAttributes(JNIEnv* env, jclass clazz,
+ jobject tokenObj) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+
+ ui::PixelFormat format;
+ ui::Dataspace dataspace;
+ uint8_t componentMask;
+ status_t err = SurfaceComposerClient::getDisplayedContentSamplingAttributes(
+ token, &format, &dataspace, &componentMask);
+ if (err != OK) {
+ return nullptr;
+ }
+ return env->NewObject(gDisplayedContentSamplingAttributesClassInfo.clazz,
+ gDisplayedContentSamplingAttributesClassInfo.ctor,
+ format, dataspace, componentMask);
+}
+
+static jboolean nativeSetDisplayedContentSamplingEnabled(JNIEnv* env, jclass clazz,
+ jobject tokenObj, jboolean enable, jint componentMask, jint maxFrames) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ return SurfaceComposerClient::setDisplayContentSamplingEnabled(
+ token, enable, componentMask, maxFrames);
+}
+
+static jobject nativeGetDisplayedContentSample(JNIEnv* env, jclass clazz, jobject tokenObj,
+ jlong maxFrames, jlong timestamp) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+
+ DisplayedFrameStats stats;
+ status_t err = SurfaceComposerClient::getDisplayedContentSample(
+ token, maxFrames, timestamp, &stats);
+ if (err != OK) {
+ return nullptr;
+ }
+
+ jlongArray histogramComponent0 = env->NewLongArray(stats.component_0_sample.size());
+ jlongArray histogramComponent1 = env->NewLongArray(stats.component_1_sample.size());
+ jlongArray histogramComponent2 = env->NewLongArray(stats.component_2_sample.size());
+ jlongArray histogramComponent3 = env->NewLongArray(stats.component_3_sample.size());
+ if ((histogramComponent0 == nullptr) ||
+ (histogramComponent1 == nullptr) ||
+ (histogramComponent2 == nullptr) ||
+ (histogramComponent3 == nullptr)) {
+ return JNI_FALSE;
+ }
+
+ env->SetLongArrayRegion(histogramComponent0, 0,
+ stats.component_0_sample.size(),
+ reinterpret_cast<jlong*>(stats.component_0_sample.data()));
+ env->SetLongArrayRegion(histogramComponent1, 0,
+ stats.component_1_sample.size(),
+ reinterpret_cast<jlong*>(stats.component_1_sample.data()));
+ env->SetLongArrayRegion(histogramComponent2, 0,
+ stats.component_2_sample.size(),
+ reinterpret_cast<jlong*>(stats.component_2_sample.data()));
+ env->SetLongArrayRegion(histogramComponent3, 0,
+ stats.component_3_sample.size(),
+ reinterpret_cast<jlong*>(stats.component_3_sample.data()));
+ return env->NewObject(gDisplayedContentSampleClassInfo.clazz,
+ gDisplayedContentSampleClassInfo.ctor,
+ stats.numFrames,
+ histogramComponent0,
+ histogramComponent1,
+ histogramComponent2,
+ histogramComponent3);
+}
+
static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj,
jboolean secure) {
ScopedUtfChars name(env, nameObj);
@@ -955,6 +1033,14 @@
(void*)nativeCaptureLayers },
{"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
(void*)nativeSetInputWindowInfo },
+ {"nativeGetDisplayedContentSamplingAttributes",
+ "(Landroid/os/IBinder;)Landroid/hardware/display/DisplayedContentSamplingAttributes;",
+ (void*)nativeGetDisplayedContentSamplingAttributes },
+ {"nativeSetDisplayedContentSamplingEnabled", "(Landroid/os/IBinder;ZII)Z",
+ (void*)nativeSetDisplayedContentSamplingEnabled },
+ {"nativeGetDisplayedContentSample",
+ "(Landroid/os/IBinder;JJ)Landroid/hardware/display/DisplayedContentSample;",
+ (void*)nativeGetDisplayedContentSample },
};
int register_android_view_SurfaceControl(JNIEnv* env)
@@ -1009,6 +1095,18 @@
gGraphicBufferClassInfo.builder = GetStaticMethodIDOrDie(env, graphicsBufferClazz,
"createFromExisting", "(IIIIJ)Landroid/graphics/GraphicBuffer;");
+ jclass displayedContentSampleClazz = FindClassOrDie(env,
+ "android/hardware/display/DisplayedContentSample");
+ gDisplayedContentSampleClassInfo.clazz = MakeGlobalRefOrDie(env, displayedContentSampleClazz);
+ gDisplayedContentSampleClassInfo.ctor = GetMethodIDOrDie(env,
+ displayedContentSampleClazz, "<init>", "(J[J[J[J[J)V");
+
+ jclass displayedContentSamplingAttributesClazz = FindClassOrDie(env,
+ "android/hardware/display/DisplayedContentSamplingAttributes");
+ gDisplayedContentSamplingAttributesClassInfo.clazz = MakeGlobalRefOrDie(env,
+ displayedContentSamplingAttributesClazz);
+ gDisplayedContentSamplingAttributesClassInfo.ctor = GetMethodIDOrDie(env,
+ displayedContentSamplingAttributesClazz, "<init>", "(III)V");
return err;
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index fd64c65..514f306 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -76,5 +76,11 @@
// OPEN: Settings > Developer options > Disable > Info dialog
DIALOG_DISABLE_DEVELOPMENT_OPTIONS = 1591;
+
+ // OPEN: WifiDppConfiguratorActivity (android.settings.WIFI_DPP_CONFIGURATOR_XXX action intents)
+ SETTINGS_WIFI_DPP_CONFIGURATOR = 1595;
+
+ // OPEN: WifiDppEnrolleeActivity (android.settings.WIFI_DPP_ENROLLEE_XXX action intents)
+ SETTINGS_WIFI_DPP_ENROLLEE = 1596;
}
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
index 3d60a86..528c1a4 100644
--- a/core/proto/android/server/usagestatsservice.proto
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -54,6 +54,9 @@
// Time attributes stored as an offset of the IntervalStats's beginTime.
optional int64 last_time_service_used_ms = 8;
optional int64 total_time_service_used_ms = 9;
+ // Time attributes stored as an offset of the IntervalStats's beginTime.
+ optional int64 last_time_visible_ms = 10;
+ optional int64 total_time_visible_ms = 11;
}
// Stores the relevant information an IntervalStats will have about a Configuration
@@ -82,6 +85,9 @@
optional string notification_channel = 12;
// notification_channel_index contains the index + 1 of the channel name in the string pool
optional int32 notification_channel_index = 13;
+ // If class field is an Activity, instance_id is a unique id of the
+ // Activity object.
+ optional int32 instance_id = 14;
}
// The following fields contain supplemental data used to build IntervalStats, such as a string
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 988eac0..77efbec 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -897,7 +897,7 @@
android:protectionLevel="dangerous"
android:usageInfoRequired="true" />
- <!-- @hide @SystemApi
+ <!-- @hide @SystemApi @TestApi
Allows an application to modify OBB files visible to other apps. -->
<permission android:name="android.permission.WRITE_OBB"
android:protectionLevel="signature|privileged" />
@@ -1120,6 +1120,18 @@
android:description="@string/permdesc_manageOwnCalls"
android:protectionLevel="normal" />
+ <!--Allows an app which implements the
+ {@link InCallService} API to be eligible to be enabled as a calling companion app. This
+ means that the Telecom framework will bind to the app's InCallService implementation when
+ there are calls active. The app can use the InCallService API to view information about
+ calls on the system and control these calls.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.CALL_COMPANION_APP"
+ android:label="@string/permlab_callCompanionApp"
+ android:description="@string/permdesc_callCompanionApp"
+ android:protectionLevel="normal" />
+
<!-- Allows a calling app to continue a call which was started in another app. An example is a
video calling app that wants to continue a voice call on the user's mobile network.<p>
When the handover of a call from one app to another takes place, there are two devices
@@ -1368,7 +1380,7 @@
<!-- ================================== -->
<eat-comment />
- <!-- @SystemApi Allows an application (Phone) to send a request to other applications
+ <!-- Allows an application (Phone) to send a request to other applications
to handle the respond-via-message action during incoming calls.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"
@@ -1452,7 +1464,7 @@
android:description="@string/permdesc_accessLocationExtraCommands"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows an application to install a location provider into the Location Manager.
+ <!-- Allows an application to install a location provider into the Location Manager.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
android:protectionLevel="signature|privileged" />
@@ -1463,7 +1475,7 @@
<permission android:name="android.permission.HDMI_CEC"
android:protectionLevel="signature|privileged|vendorPrivileged" />
- <!-- @SystemApi Allows an application to use location features in hardware,
+ <!-- Allows an application to use location features in hardware,
such as the geofencing api.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.LOCATION_HARDWARE"
@@ -1658,7 +1670,7 @@
android:label="@string/permlab_bluetoothAdmin"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows applications to pair bluetooth devices without user interaction, and to
+ <!-- Allows applications to pair bluetooth devices without user interaction, and to
allow or disallow phonebook access or message access.
This is not available to third party applications. -->
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
@@ -1749,7 +1761,7 @@
android:usageInfoRequired="true" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
- <!-- @SystemApi Allows applications to call into AccountAuthenticators.
+ <!-- Allows applications to call into AccountAuthenticators.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCOUNT_MANAGER"
android:protectionLevel="signature" />
@@ -1928,7 +1940,7 @@
<!-- =========================================== -->
<eat-comment />
- <!-- @SystemApi Allows modification of the telephony state - power on, mmi, etc.
+ <!-- Allows modification of the telephony state - power on, mmi, etc.
Does not include placing calls.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MODIFY_PHONE_STATE"
@@ -2087,17 +2099,15 @@
<p>This permission should <em>only</em> be requested by the platform
document management app. This permission cannot be granted to
third-party apps.
- <p>Protection level: signature
-->
<permission android:name="android.permission.MANAGE_DOCUMENTS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|documenter" />
<!-- @hide Allows an application to cache content.
<p>Not for use by third-party applications.
- <p>Protection level: signature
-->
<permission android:name="android.permission.CACHE_CONTENT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|documenter" />
<!-- @SystemApi @hide
Allows an application to aggressively allocate disk space.
@@ -2128,6 +2138,15 @@
android:label="@string/permlab_disableKeyguard"
android:protectionLevel="normal" />
+ <!-- Allows an application to get the screen lock complexity and prompt users to update the
+ screen lock to a certain complexity level.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY"
+ android:label="@string/permlab_getAndRequestScreenLockComplexity"
+ android:description="@string/permdesc_getAndRequestScreenLockComplexity"
+ android:protectionLevel="normal" />
+
<!-- ================================== -->
<!-- Permissions to access other installed applications -->
<!-- ================================== -->
@@ -2213,9 +2232,9 @@
android:description="@string/permdesc_reorderTasks"
android:protectionLevel="normal" />
- <!-- @hide Allows an application to change to remove/kill tasks -->
+ <!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks -->
<permission android:name="android.permission.REMOVE_TASKS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|documenter" />
<!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
@@ -2372,7 +2391,7 @@
<!-- ============================================ -->
<eat-comment />
- <!-- @SystemApi Allows applications to set the system time.
+ <!-- Allows applications to set the system time.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_TIME"
android:protectionLevel="signature|privileged" />
@@ -2462,7 +2481,7 @@
<permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
android:protectionLevel="signature" />
- <!-- @SystemApi @TestApi Allows an application to modify the current configuration, such
+ <!-- Allows an application to modify the current configuration, such
as locale. -->
<permission android:name="android.permission.CHANGE_CONFIGURATION"
android:protectionLevel="signature|privileged|development" />
@@ -2484,7 +2503,7 @@
android:description="@string/permdesc_writeSettings"
android:protectionLevel="signature|preinstalled|appop|pre23" />
- <!-- @SystemApi Allows an application to modify the Google service map.
+ <!-- Allows an application to modify the Google service map.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_GSERVICES"
android:protectionLevel="signature|privileged" />
@@ -2500,7 +2519,7 @@
<permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Modify the global animation scaling factor.
+ <!-- Modify the global animation scaling factor.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_ANIMATION_SCALE"
android:protectionLevel="signature|privileged|development" />
@@ -2555,12 +2574,12 @@
android:description="@string/permdesc_broadcastSticky"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows mounting and unmounting file systems for removable storage.
+ <!-- Allows mounting and unmounting file systems for removable storage.
<p>Not for use by third-party applications.-->
<permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows formatting file systems for removable storage.
+ <!-- Allows formatting file systems for removable storage.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
android:protectionLevel="signature|privileged" />
@@ -2594,7 +2613,7 @@
<permission android:name="android.permission.ASEC_RENAME"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows applications to write the apn settings and read sensitive fields of
+ <!-- Allows applications to write the apn settings and read sensitive fields of
an existing apn settings like user and password.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_APN_SETTINGS"
@@ -2695,40 +2714,40 @@
<!-- ========================================= -->
<eat-comment />
- <!-- @SystemApi Allows an application to read or write the secure system settings.
+ <!-- Allows an application to read or write the secure system settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_SECURE_SETTINGS"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Allows an application to retrieve state dump information from system services.
+ <!-- Allows an application to retrieve state dump information from system services.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DUMP"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Allows an application to read the low-level system log files.
+ <!-- Allows an application to read the low-level system log files.
<p>Not for use by third-party applications, because
Log entries can contain the user's private information. -->
<permission android:name="android.permission.READ_LOGS"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Configure an application for debugging.
+ <!-- Configure an application for debugging.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_DEBUG_APP"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Allows an application to set the maximum number of (not needed)
+ <!-- Allows an application to set the maximum number of (not needed)
application processes that can be running.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_PROCESS_LIMIT"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Allows an application to control whether activities are immediately
+ <!-- Allows an application to control whether activities are immediately
finished when put in the background.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_ALWAYS_FINISH"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi Allow an application to request that a signal be sent to all persistent processes.
+ <!-- Allow an application to request that a signal be sent to all persistent processes.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES"
android:protectionLevel="signature|privileged|development" />
@@ -2738,7 +2757,7 @@
<!-- ==================================== -->
<eat-comment />
- <!-- @SystemApi Allows access to the list of accounts in the Accounts Service. -->
+ <!-- Allows access to the list of accounts in the Accounts Service. -->
<permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"
android:protectionLevel="signature|privileged" />
@@ -2747,12 +2766,12 @@
<permission android:name="android.permission.GET_PASSWORD"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows applications to RW to diagnostic resources.
+ <!-- Allows applications to RW to diagnostic resources.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DIAGNOSTIC"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to open, close, or disable the status bar
+ <!-- Allows an application to open, close, or disable the status bar
and its icons.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.STATUS_BAR"
@@ -2777,7 +2796,7 @@
<permission android:name="android.permission.FORCE_BACK"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to update device statistics.
+ <!-- Allows an application to update device statistics.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.UPDATE_DEVICE_STATS"
android:protectionLevel="signature|privileged" />
@@ -3224,7 +3243,7 @@
android:description="@string/permdesc_requestDeletePackages"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows an application to install packages.
+ <!-- Allows an application to install packages.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_PACKAGES"
android:protectionLevel="signature|privileged" />
@@ -3297,7 +3316,7 @@
<permission android:name="android.permission.FORCE_PERSISTABLE_URI_PERMISSIONS"
android:protectionLevel="signature" />
- <!-- @SystemApi Old permission for deleting an app's cache files, no longer used,
+ <!-- Old permission for deleting an app's cache files, no longer used,
but signals for us to quietly ignore calls instead of throwing an exception. -->
<permission android:name="android.permission.DELETE_CACHE_FILES"
android:protectionLevel="signature|privileged" />
@@ -3307,7 +3326,7 @@
<permission android:name="android.permission.INTERNAL_DELETE_CACHE_FILES"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to delete packages.
+ <!-- Allows an application to delete packages.
<p>Not for use by third-party applications.
<p>Starting in {@link android.os.Build.VERSION_CODES#N}, user confirmation is requested
when the application deleting the package is not the same application that installed the
@@ -3320,7 +3339,7 @@
<permission android:name="android.permission.MOVE_PACKAGE"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to change whether an application component (other than its own) is
+ <!-- Allows an application to change whether an application component (other than its own) is
enabled or not.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
@@ -3351,6 +3370,11 @@
<permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
android:protectionLevel="signature|installer" />
+ <!-- @SystemApi Allows an application to observe role holder changes.
+ @hide -->
+ <permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
+ android:protectionLevel="signature|installer" />
+
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide
@@ -3443,7 +3467,7 @@
android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.CONTROL_VPN" />
- <!-- @SystemApi Allows an application to capture audio output.
+ <!-- Allows an application to capture audio output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"
android:protectionLevel="signature|privileged" />
@@ -3481,7 +3505,7 @@
<permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to know what content is playing and control its playback.
+ <!-- Allows an application to know what content is playing and control its playback.
<p>Not for use by third-party applications due to privacy of media consumption</p> -->
<permission android:name="android.permission.MEDIA_CONTENT_CONTROL"
android:protectionLevel="signature|privileged" />
@@ -3508,7 +3532,7 @@
<permission android:name="android.permission.BRICK"
android:protectionLevel="signature" />
- <!-- @SystemApi Required to be able to reboot the device.
+ <!-- Required to be able to reboot the device.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.REBOOT"
android:protectionLevel="signature|privileged" />
@@ -3569,11 +3593,11 @@
<permission android:name="android.permission.BROADCAST_NETWORK_PRIVILEGED"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Not for use by third-party applications. -->
+ <!-- Not for use by third-party applications. -->
<permission android:name="android.permission.MASTER_CLEAR"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to call any phone number, including emergency
+ <!-- Allows an application to call any phone number, including emergency
numbers, without going through the Dialer user interface for the user
to confirm the call being placed.
<p>Not for use by third-party applications. -->
@@ -3588,19 +3612,19 @@
<permission android:name="android.permission.PERFORM_SIM_ACTIVATION"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows enabling/disabling location update notifications from
+ <!-- Allows enabling/disabling location update notifications from
the radio.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows read/write access to the "properties" table in the checkin
+ <!-- Allows read/write access to the "properties" table in the checkin
database, to change values that get uploaded.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to collect component usage
+ <!-- Allows an application to collect component usage
statistics
<p>Declaring the permission implies intention to use the API and the user of the
device can grant permission through the Settings application. -->
@@ -3633,7 +3657,7 @@
android:description="@string/permdesc_requestIgnoreBatteryOptimizations"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows an application to collect battery statistics -->
+ <!-- Allows an application to collect battery statistics -->
<permission android:name="android.permission.BATTERY_STATS"
android:protectionLevel="signature|privileged|development" />
@@ -3662,12 +3686,12 @@
<permission android:name="android.permission.CONFIRM_FULL_BACKUP"
android:protectionLevel="signature" />
- <!-- @SystemApi Must be required by a {@link android.widget.RemoteViewsService},
+ <!-- Must be required by a {@link android.widget.RemoteViewsService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_REMOTEVIEWS"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to tell the AppWidget service which application
+ <!-- Allows an application to tell the AppWidget service which application
can access AppWidget's data. The normal user flow is that a user
picks an AppWidget to go into a particular host, thereby giving that
host application access to the private data from the AppWidget app.
@@ -3698,7 +3722,7 @@
<permission android:name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"
android:protectionLevel="signature" />
- <!-- @SystemApi This permission can be used on content providers to allow the global
+ <!-- This permission can be used on content providers to allow the global
search system to access their data. Typically it used when the
provider has some permissions protecting it (which global search
would not be expected to hold), and added as a read-only permission
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 56c2e3e..f3b4df7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1086,7 +1086,7 @@
<string name="email" msgid="4560673117055050403">"ኢሜይል"</string>
<string name="email_desc" msgid="3638665569546416795">"ለተመረጡ አድራሻዎች ኢሜይል ላክ"</string>
<string name="dial" msgid="1253998302767701559">"ጥሪ"</string>
- <string name="dial_desc" msgid="6573723404985517250">"ወደተመረጠውን ስልክ ቁጥር ደውል"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"ወደ ተመረጠው ስልክ ቁጥር ደውል"</string>
<string name="map" msgid="5441053548030107189">"ካርታ"</string>
<string name="map_desc" msgid="1836995341943772348">"የተመረጠውን አድራሻ ያለበትን አግኝ"</string>
<string name="browse" msgid="1245903488306147205">"ክፈት"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 2af43ce..a14a871 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -537,7 +537,7 @@
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Fingerabdruck teilweise erkannt. Bitte versuche es noch einmal."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingerabdruck konnte nicht verarbeitet werden. Bitte versuche es noch einmal."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerabdrucksensor ist verschmutzt. Reinige ihn und versuche es noch einmal."</string>
- <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Finger zu schnell bewegt. Bitte versuche es noch einmal."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Finger zu schnell bewegt, bitte noch einmal versuchen"</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger zu langsam bewegt. Bitte versuche es noch einmal."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
@@ -549,7 +549,7 @@
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Zeitüberschreitung für Fingerabdruck. Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Fingerabdruckvorgang abgebrochen"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen."</string>
- <string name="fingerprint_error_lockout" msgid="5536934748136933450">"Zu viele Versuche. Bitte versuche es später noch einmal."</string>
+ <string name="fingerprint_error_lockout" msgid="5536934748136933450">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Zu viele Versuche. Der Fingerabdrucksensor wurde deaktiviert."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"Keine Fingerabdrücke erfasst."</string>
@@ -581,7 +581,7 @@
<string name="face_error_no_space" msgid="8224993703466381314">"Gesicht kann nicht gespeichert werden."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Gesichtserkennung abgebrochen."</string>
<string name="face_error_user_canceled" msgid="8943921120862164539">"Gesichtsauthentifizierung vom Nutzer abgebrochen."</string>
- <string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche. Versuch es später noch einmal."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Zu viele Versuche. Gesichtserkennung deaktiviert."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Versuch es noch einmal."</string>
<string name="face_error_not_enrolled" msgid="9166792142679691323">"Kein Gesicht erfasst."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 77a8f05..e633ab4 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1084,27 +1084,27 @@
<string name="inputMethod" msgid="1653630062304567879">"Sisestusmeetod"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstitoimingud"</string>
<string name="email" msgid="4560673117055050403">"E-post"</string>
- <string name="email_desc" msgid="3638665569546416795">"Valitud aadressile meili saatmine"</string>
+ <string name="email_desc" msgid="3638665569546416795">"Saada valitud aadressile meil"</string>
<string name="dial" msgid="1253998302767701559">"Helista"</string>
- <string name="dial_desc" msgid="6573723404985517250">"Valitud telefoninumbrile helistamine"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"Helista valitud telefoninumbrile"</string>
<string name="map" msgid="5441053548030107189">"Kaart"</string>
- <string name="map_desc" msgid="1836995341943772348">"Valitud aadressi leidmine"</string>
+ <string name="map_desc" msgid="1836995341943772348">"Leia valitud aadress"</string>
<string name="browse" msgid="1245903488306147205">"Ava"</string>
- <string name="browse_desc" msgid="8220976549618935044">"Valitud URL-i avamine"</string>
+ <string name="browse_desc" msgid="8220976549618935044">"Ava valitud URL"</string>
<string name="sms" msgid="4560537514610063430">"Saada sõnum"</string>
- <string name="sms_desc" msgid="7526588350969638809">"Valitud telefoninumbrile sõnumi saatmine"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"Saada valitud telefoninumbrile sõnum"</string>
<string name="add_contact" msgid="7867066569670597203">"Lisa"</string>
- <string name="add_contact_desc" msgid="4830217847004590345">"Kontaktide hulka lisamine"</string>
+ <string name="add_contact_desc" msgid="4830217847004590345">"Lisa kontaktide hulka"</string>
<string name="view_calendar" msgid="979609872939597838">"Kuva"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Valitud aja vaatamine kalendris"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Kuva valitud aeg kalendris"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Lisa ajakavasse"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Ürituse ajastamine valitud ajale"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Ajasta üritus valitud ajale"</string>
<string name="view_flight" msgid="7691640491425680214">"Jälgi"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"Valitud lennu jälgimine"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"Jälgi valitud lendu"</string>
<string name="translate" msgid="9218619809342576858">"Tõlgi"</string>
- <string name="translate_desc" msgid="4502367770068777202">"Tõlkige valitud tekst"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Tõlgi valitud tekst"</string>
<string name="define" msgid="7394820043869954211">"Defineeri"</string>
- <string name="define_desc" msgid="7910883642444919726">"Valitud teksti defineerimine"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Defineeri valitud tekst"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Talletusruum saab täis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index bc367be..3f8f0d1 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1097,7 +1097,7 @@
<string name="add_contact_desc" msgid="4830217847004590345">"Lisää yhteystietoihin"</string>
<string name="view_calendar" msgid="979609872939597838">"Näytä"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Näytä valittu aika kalenterissa"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Aikataulu"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Aikatauluta"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Ajoita tapahtuma valitulle ajalle"</string>
<string name="view_flight" msgid="7691640491425680214">"Seuraa"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Seuraa valittua lentoa"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index fccb289..87e1f97 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -306,7 +306,7 @@
<string name="permgroupdesc_calllog" msgid="3006237336748283775">"कॉल लॉग की जानकारी देखना और उसमें बदलाव करना"</string>
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को अपने काॅल लाॅग एक्सेस करने की मंज़ूरी देना चाहते हैं?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"फ़ोन"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"फ़ोन कॉल करें और प्रबंधित करें"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति दें"</string>
<string name="permgrouprequest_phone" msgid="9166979577750581037">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति दें?"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"शरीर संवेदक"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"अपने महत्वपूर्ण संकेतों के बारे में सेंसर डेटा को ऐक्सेस करें"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 69b4db2..cd2af7b 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1097,7 +1097,7 @@
<string name="add_contact_desc" msgid="4830217847004590345">"បញ្ចូលទៅក្នុងទំនាក់ទំនង"</string>
<string name="view_calendar" msgid="979609872939597838">"មើល"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"មើលពេលវេលាដែលបានជ្រើសរើសនៅក្នុងប្រតិទិន"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"កាលវិភាគ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"កំណត់កាលវិភាគ"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"កំណត់កាលវិភាគព្រឹត្តិការណ៍សម្រាប់ពេលវេលាដែលបានជ្រើសរើស"</string>
<string name="view_flight" msgid="7691640491425680214">"តាមដាន"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"តាមដានជើងហោះហើរដែលបានជ្រើសរើស"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 2232f4d..f6e9b72 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1083,15 +1083,15 @@
<string name="deleteText" msgid="6979668428458199034">"Жок кылуу"</string>
<string name="inputMethod" msgid="1653630062304567879">"Киргизүү ыкмасы"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Текст боюнча иштер"</string>
- <string name="email" msgid="4560673117055050403">"Электрондук почта"</string>
+ <string name="email" msgid="4560673117055050403">"Кат жөнөтүү"</string>
<string name="email_desc" msgid="3638665569546416795">"Тандалган дарекке электрондук кат жөнөтүү"</string>
<string name="dial" msgid="1253998302767701559">"Чалуу"</string>
<string name="dial_desc" msgid="6573723404985517250">"Тандалган телефон номерине чалуу"</string>
- <string name="map" msgid="5441053548030107189">"Карта"</string>
+ <string name="map" msgid="5441053548030107189">"Картадан кароо"</string>
<string name="map_desc" msgid="1836995341943772348">"Тандалган даректи картада табуу"</string>
<string name="browse" msgid="1245903488306147205">"Ачуу"</string>
<string name="browse_desc" msgid="8220976549618935044">"Тандалган URL\'ди ачуу"</string>
- <string name="sms" msgid="4560537514610063430">"Билдирүү"</string>
+ <string name="sms" msgid="4560537514610063430">"Билдирүү жазуу"</string>
<string name="sms_desc" msgid="7526588350969638809">"Тандалган телефон номерине билдирүү жөнөтүү"</string>
<string name="add_contact" msgid="7867066569670597203">"Кошуу"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Байланыштарга кошуу"</string>
@@ -1103,7 +1103,7 @@
<string name="view_flight_desc" msgid="3876322502674253506">"Тандалган аба каттамына көз салуу"</string>
<string name="translate" msgid="9218619809342576858">"Которуу"</string>
<string name="translate_desc" msgid="4502367770068777202">"Тандалган текстти которуу"</string>
- <string name="define" msgid="7394820043869954211">"Төмөнкү сөздүн аныктамасын бериңиз:"</string>
+ <string name="define" msgid="7394820043869954211">"Аныктоо"</string>
<string name="define_desc" msgid="7910883642444919726">"Тандалган текстти аныктоо"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Сактагычта орун калбай баратат"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Системанын кээ бир функциялары иштебеши мүмкүн"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index eec4b31..dd4b125 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1107,7 +1107,7 @@
<string name="email_desc" msgid="3638665569546416795">"Nosūtīt e-pasta ziņojumu uz atlasīto adresi"</string>
<string name="dial" msgid="1253998302767701559">"Zvanīt"</string>
<string name="dial_desc" msgid="6573723404985517250">"Zvanīt uz atlasīto tālruņa numuru"</string>
- <string name="map" msgid="5441053548030107189">"Maps"</string>
+ <string name="map" msgid="5441053548030107189">"Karte"</string>
<string name="map_desc" msgid="1836995341943772348">"Atrast atlasīto adresi"</string>
<string name="browse" msgid="1245903488306147205">"Atvērt"</string>
<string name="browse_desc" msgid="8220976549618935044">"Atvērt atlasīto URL"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index ac28270..93f7e37 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1083,23 +1083,23 @@
<string name="deleteText" msgid="6979668428458199034">"Устгах"</string>
<string name="inputMethod" msgid="1653630062304567879">"Оруулах арга"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Текст үйлдэл"</string>
- <string name="email" msgid="4560673117055050403">"Имэйл"</string>
+ <string name="email" msgid="4560673117055050403">"Имэйл бичих"</string>
<string name="email_desc" msgid="3638665569546416795">"Сонгосон хаяг руу имэйл илгээх"</string>
<string name="dial" msgid="1253998302767701559">"Залгах"</string>
<string name="dial_desc" msgid="6573723404985517250">"Сонгосон утасны дугаар руу залгах"</string>
- <string name="map" msgid="5441053548030107189">"Газрын зураг"</string>
+ <string name="map" msgid="5441053548030107189">"Газрын зураг хийх"</string>
<string name="map_desc" msgid="1836995341943772348">"Сонгосон хаягийг байршуулах"</string>
<string name="browse" msgid="1245903488306147205">"Нээх"</string>
<string name="browse_desc" msgid="8220976549618935044">"Сонгосон URL-г нээх"</string>
- <string name="sms" msgid="4560537514610063430">"Зурвас"</string>
+ <string name="sms" msgid="4560537514610063430">"Зурвас бичих"</string>
<string name="sms_desc" msgid="7526588350969638809">"Сонгосон утасны дугаар руу мессеж илгээх"</string>
<string name="add_contact" msgid="7867066569670597203">"Нэмэх"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Харилцагчид нэмэх"</string>
<string name="view_calendar" msgid="979609872939597838">"Үзэх"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Календариас сонгосон огноог харах"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Хуваарь"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Хуваарь гаргах"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Aрга хэмжээг сонгосон цагт хуваарилах"</string>
- <string name="view_flight" msgid="7691640491425680214">"Бичлэг"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Хянах"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Сонгосон нислэгийг хянах"</string>
<string name="translate" msgid="9218619809342576858">"Орчуулах"</string>
<string name="translate_desc" msgid="4502367770068777202">"Сонгосон текстийг орчуулах"</string>
diff --git a/core/res/res/values-mr-watch/strings.xml b/core/res/res/values-mr-watch/strings.xml
index 223f8fa..f8a5214 100644
--- a/core/res/res/values-mr-watch/strings.xml
+++ b/core/res/res/values-mr-watch/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप"</string>
+ <string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"सेन्सर"</string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 9a58b38..902380b 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -183,7 +183,7 @@
<string name="ssl_ca_cert_noti_by_administrator" msgid="3541729986326153557">"आपल्या कार्य प्रोफाइल प्रशासकाद्वारे"</string>
<string name="ssl_ca_cert_noti_managed" msgid="4030263497686867141">"<xliff:g id="MANAGING_DOMAIN">%s</xliff:g> द्वारे"</string>
<string name="work_profile_deleted" msgid="5005572078641980632">"कार्य प्रोफाईल हटविले"</string>
- <string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफाइल प्रशासक अॅप गहाळ आहे किंवा करप्ट आहे. परिणामी, तुमचे कार्य प्रोफाइल आणि संबंधित डेटा हटवले गेले आहेत. सहाय्यासाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
+ <string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफाइल प्रशासक अॅप गहाळ आहे किंवा करप्ट आहे. परिणामी, तुमचे कार्य प्रोफाइल आणि संबंधित डेटा हटवले गेले आहेत. सहाय्यासाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"तुमचे कार्य प्रोफाइल आता या डिव्हाइसवर उपलब्ध नाही"</string>
<string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"बर्याचदा पासवर्ड टाकण्याचा प्रयत्न केला"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"डिव्हाइस व्यवस्थापित केले आहे"</string>
@@ -281,9 +281,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"या डिव्हाइसच्या स्थानावर प्रवेश"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला या डिव्हाइसचे स्थान अॅक्सेस करू द्यायचे?"</string>
- <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"तुम्ही अॅप वापरत असताना, अॅपला फक्त स्थानाचा अॅक्सेस असेल."</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"तुम्ही अॅप वापरत असताना, अॅपला फक्त स्थानाचा अॅक्सेस असेल."</string>
<string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"या डिव्हाइसचे स्थान अॅक्सेस करण्याची <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला नेहेमी अनुमती द्यायची का?"</string>
- <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"तुम्ही अॅप वापरत नसलात तरीही, अॅपला स्थानाचा नेहेमी अॅक्सेस असेल."</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"तुम्ही अॅप वापरत नसलात तरीही, अॅपला स्थानाचा नेहेमी अॅक्सेस असेल."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"कॅलेंडर"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"आपल्या कॅलेंडरवर प्रवेश"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला तुमचे कॅलेंडर अॅक्सेस करू द्यायचे?"</string>
@@ -330,11 +330,11 @@
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"फिंगरप्रिंट जेश्चर"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"डिव्हाइसच्या फिंगरप्रिंट सेंसरवर केलेले जेश्चर कॅप्चर करू शकते."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्टेटस बार अक्षम करा किंवा सुधारित करा"</string>
- <string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"स्टेटस बार होऊ द्या"</string>
- <string name="permdesc_statusBarService" msgid="716113660795976060">"स्टेटस बार होण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_statusBarService" msgid="716113660795976060">"स्टेटस बार होण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"स्टेटस बार विस्तृत करा/संकुचित करा"</string>
- <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"स्टेटस बार विस्तृत करण्यासाठी किंवा संक्षिप्त करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"स्टेटस बार विस्तृत करण्यासाठी किंवा संक्षिप्त करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_install_shortcut" msgid="4279070216371564234">"शॉर्टकट स्थापित करा"</string>
<string name="permdesc_install_shortcut" msgid="8341295916286736996">"अनुप्रयोगाला वापरकर्ता हस्तक्षेपाशिवाय मुख्यस्क्रीन शॉर्टकट जोडण्याची अनुमती देते."</string>
<string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"शॉर्टकट विस्थापित करा"</string>
@@ -344,103 +344,103 @@
<string name="permlab_answerPhoneCalls" msgid="4077162841226223337">"फोन कॉलचे उत्तर द्या"</string>
<string name="permdesc_answerPhoneCalls" msgid="2901889867993572266">"येणार्या फोन कॉलचे उत्तर देण्यास अॅपला अनुमती देते."</string>
<string name="permlab_receiveSms" msgid="8673471768947895082">"मजकूर मेसेज मिळवा (SMS)"</string>
- <string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS मेसेज प्राप्त करण्याची आणि त्यावर प्रक्रिया करण्याची अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डीव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
+ <string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS मेसेज प्राप्त करण्याची आणि त्यावर प्रक्रिया करण्याची अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डीव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"मजकूर मेसेज मिळवा (MMS)"</string>
- <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डिव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
+ <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डिव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"सेल प्रसारण मेसेज वाचा"</string>
- <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अॅप ला अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अॅप्स व्यत्यय आणू शकतात."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अॅप ला अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अॅप्स व्यत्यय आणू शकतात."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"सदस्यता घेतलेली फीड वाचा"</string>
- <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"सध्या संकालित केलेल्या फीडविषयी तपशील मिळविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"सध्या संकालित केलेल्या फीडविषयी तपशील मिळविण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_sendSms" msgid="7544599214260982981">"SMS मेसेज पाठवणे आणि पाहणे"</string>
- <string name="permdesc_sendSms" msgid="7094729298204937667">"SMS मेसेज पाठविण्यासाठी अॅप ला अनुमती देते. हे अनपेक्षित शुल्कामुळे होऊ शकते. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय मेसेज पाठवल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
+ <string name="permdesc_sendSms" msgid="7094729298204937667">"SMS मेसेज पाठविण्यासाठी अॅप ला अनुमती देते. हे अनपेक्षित शुल्कामुळे होऊ शकते. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय मेसेज पाठवल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"तुमचे मजकूर मेसेज वाचा (SMS किंवा MMS)"</string>
- <string name="permdesc_readSms" product="tablet" msgid="4741697454888074891">"हा अॅप तुमच्या टॅब्लेटवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
- <string name="permdesc_readSms" product="tv" msgid="5796670395641116592">"हा अॅप तुमच्या टीव्हीवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
- <string name="permdesc_readSms" product="default" msgid="6826832415656437652">"हा अॅप तुमच्या फोनवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
+ <string name="permdesc_readSms" product="tablet" msgid="4741697454888074891">"हा अॅप तुमच्या टॅब्लेटवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
+ <string name="permdesc_readSms" product="tv" msgid="5796670395641116592">"हा अॅप तुमच्या टीव्हीवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
+ <string name="permdesc_readSms" product="default" msgid="6826832415656437652">"हा अॅप तुमच्या फोनवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
<string name="permlab_receiveWapPush" msgid="5991398711936590410">"मजकूर मेसेज मिळवा (WAP)"</string>
- <string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुम्हाला पाठविलेले मेसेज तुम्हाला न दर्शविता त्यांचे परीक्षण करण्याची आणि ते हटविण्याची क्षमता समाविष्ट करते."</string>
+ <string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुम्हाला पाठविलेले मेसेज तुम्हाला न दर्शविता त्यांचे परीक्षण करण्याची आणि ते हटविण्याची क्षमता समाविष्ट करते."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"चालणारे अॅप्स पुनर्प्राप्त करा"</string>
- <string name="permdesc_getTasks" msgid="7454215995847658102">"सध्या आणि अलीकडे चालणार्या कार्यांविषयी माहिती पुनर्प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे डिव्हाइसवर कोणते अॅप्लिकेशन वापरले जात आहेत त्याविषयी माहिती शोधण्यासाठी अॅप ला अनुमती देऊ शकतात."</string>
+ <string name="permdesc_getTasks" msgid="7454215995847658102">"सध्या आणि अलीकडे चालणार्या कार्यांविषयी माहिती पुनर्प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे डिव्हाइसवर कोणते अॅप्लिकेशन वापरले जात आहेत त्याविषयी माहिती शोधण्यासाठी अॅप ला अनुमती देऊ शकतात."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"प्रोफाईल आणि डिव्हाइस मालक व्यवस्थापित करा"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"प्रोफाईल मालक आणि डिव्हाइस मालक सेट करण्याची अॅप्सना अनुमती द्या."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"चालणारे अॅप्स पुनर्क्रमित करा"</string>
- <string name="permdesc_reorderTasks" msgid="7734217754877439351">"समोर आणि पार्श्वभूमीवर कार्ये हलविण्यासाठी अॅप ला अनुमती देते. अॅप हे आपल्या इनपुटशिवाय करू शकतो."</string>
+ <string name="permdesc_reorderTasks" msgid="7734217754877439351">"समोर आणि पार्श्वभूमीवर कार्ये हलविण्यासाठी अॅप ला अनुमती देते. अॅप हे आपल्या इनपुटशिवाय करू शकतो."</string>
<string name="permlab_enableCarMode" msgid="5684504058192921098">"कार मोड सुरू करा"</string>
- <string name="permdesc_enableCarMode" msgid="4853187425751419467">"कार मोड सक्षम करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_enableCarMode" msgid="4853187425751419467">"कार मोड सक्षम करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"अन्य अॅप्स बंद करा"</string>
- <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"अन्य अॅप्सच्या पार्श्वभूमी प्रक्रिया समाप्त करण्यासाठी अॅप ला अनुमती देते. यामुळे अन्य अॅप्स चालणे थांबू शकते."</string>
- <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"हा अॅप इतर अॅप्सच्या शीर्षस्थानी दिसू शकतो."</string>
- <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"हे अॅप इतर अॅप्सच्या शीर्षस्थानी किंवा स्क्रीनच्या इतर भागांवर दिसू शकतो. हे सामान्य अॅप वापरात व्यत्यय आणू शकते किंवा इतर अॅप्सची डिस्प्ले पद्धत बदलू शकते."</string>
+ <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"अन्य अॅप्सच्या पार्श्वभूमी प्रक्रिया समाप्त करण्यासाठी अॅप ला अनुमती देते. यामुळे अन्य अॅप्स चालणे थांबू शकते."</string>
+ <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"हा अॅप इतर अॅप्सच्या शीर्षस्थानी दिसू शकतो."</string>
+ <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"हे अॅप इतर अॅप्सच्या शीर्षस्थानी किंवा स्क्रीनच्या इतर भागांवर दिसू शकतो. हे सामान्य अॅप वापरात व्यत्यय आणू शकते किंवा इतर अॅप्सची डिस्प्ले पद्धत बदलू शकते."</string>
<string name="permlab_runInBackground" msgid="7365290743781858803">"पार्श्वभूमीत चालवा"</string>
- <string name="permdesc_runInBackground" msgid="7370142232209999824">"हे अॅप पार्श्वभूमीत चालू शकते. हे बॅटरी अधिक जलद संपवू शकते."</string>
+ <string name="permdesc_runInBackground" msgid="7370142232209999824">"हे अॅप पार्श्वभूमीत चालू शकते. हे बॅटरी अधिक जलद संपवू शकते."</string>
<string name="permlab_useDataInBackground" msgid="8694951340794341809">"पार्श्वभूमीत डेटा वापरा"</string>
<string name="permdesc_useDataInBackground" msgid="6049514223791806027">"हे अॅप पार्श्वभूमीत डेटा वापरू शकते. हे डेटाचा वापर वाढवू शकते."</string>
- <string name="permlab_persistentActivity" msgid="8841113627955563938">"अॅप नेहमी चालवा"</string>
- <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"अॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे टॅबलेट धीमा करून अन्य अॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
+ <string name="permlab_persistentActivity" msgid="8841113627955563938">"अॅप नेहमी चालवा"</string>
+ <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"अॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे टॅबलेट धीमा करून अन्य अॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"अॅपला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यासाठी अनुमती देते. हे टीव्ही धीमा करून इतर अॅप्सवर उपलब्ध असलेली मेमरी मर्यादित करू शकते."</string>
- <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"अॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे फोन धीमा करून अन्य अॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"अॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे फोन धीमा करून अन्य अॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
<string name="permlab_foregroundService" msgid="3310786367649133115">"पृष्ठभाग सेवा रन करा"</string>
<string name="permdesc_foregroundService" msgid="6471634326171344622">"अॅपला पृष्ठभाग सेवा वापरण्याची अनुमती देते."</string>
- <string name="permlab_getPackageSize" msgid="7472921768357981986">"अॅप संचयन स्थान मोजा"</string>
- <string name="permdesc_getPackageSize" msgid="3921068154420738296">"अॅप ला त्याचा कोड, डेटा आणि कॅश आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string>
+ <string name="permlab_getPackageSize" msgid="7472921768357981986">"अॅप संचयन स्थान मोजा"</string>
+ <string name="permdesc_getPackageSize" msgid="3921068154420738296">"अॅप ला त्याचा कोड, डेटा आणि कॅश आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"सिस्टम सेटिंग्ज सुधारित करा"</string>
- <string name="permdesc_writeSettings" msgid="7775723441558907181">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपल्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string>
+ <string name="permdesc_writeSettings" msgid="7775723441558907181">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपल्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string>
<string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"सुरूवातीस चालवा"</string>
- <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे टॅबलेट प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर टॅबलेटला धीमे करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे टॅबलेट प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर टॅबलेटला धीमे करण्यास अॅप ला अनुमती देते."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4525890122209673621">"सिस्टम बूट करणे समाप्त करते तसेच अॅपने स्वतः प्रारंभ करण्यास त्याला अनुमती देते. यामुळे टीव्ही प्रारंभ करण्यासाठी त्यास जास्त वेळ लागू शकतो आणि नेहमी चालू ठेवून संपूर्ण टॅबलेट धीमे करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे फोन प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर फोनला धीमे करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे फोन प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर फोनला धीमे करण्यास अॅप ला अनुमती देते."</string>
<string name="permlab_broadcastSticky" msgid="7919126372606881614">"रोचक प्रसारण पाठवा"</string>
- <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो टॅब्लेटला धीमा किंवा अस्थिर करू शकतो."</string>
+ <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो टॅब्लेटला धीमा किंवा अस्थिर करू शकतो."</string>
<string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"रोचक प्रसारणे पाठविण्यास अॅपला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर तसेच रहाते. अतिरिक्त वापर टीव्ही धीमा किंवा यासाठी बरीच मेमरी वापरली जात असल्यामुळे तो अस्थिर करू शकतो."</string>
- <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string>
+ <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"तुमचे संपर्क वाचा"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
<string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"तुम्ही विशिष्ट लोकांना इतर मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संप्रेषित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप्सला अनुमती देतात. ही परवागनी अॅप्सला तुमचा संपर्क डेटा सेव्ह करण्यासाठी अनुमती देते आणि दुर्भावनापूर्ण अॅप्स तुम्हाला न कळविता संपर्क डेटा शेअर करू शकतात."</string>
- <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
+ <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"तुमचे संपर्क सुधारित करा"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅपला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"कॉल लॉग वाचा"</string>
- <string name="permdesc_readCallLog" msgid="3204122446463552146">"हा अॅप तुमचा कॉल इतिहास वाचू शकता."</string>
+ <string name="permdesc_readCallLog" msgid="3204122446463552146">"हा अॅप तुमचा कॉल इतिहास वाचू शकता."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"कॉल लॉग लिहा"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या टीव्हीचा कॉल लॉग सुधारित करण्यासाठी अॅपला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
<string name="permlab_bodySensors" msgid="4683341291818520277">"शरीर सेंसर (हृदय गती मॉनिटरसारखे) अॅक्सेस करा"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_readCalendar" msgid="6716116972752441641">"कॅलेंडर इव्हेंट आणि तपशील वाचा"</string>
- <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"हा अॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"हा अॅप आपल्या टीव्हीवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
- <string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"हा अॅप आपल्या फोनवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+ <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"हा अॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"हा अॅप आपल्या टीव्हीवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+ <string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"हा अॅप आपल्या फोनवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
<string name="permlab_writeCalendar" msgid="8438874755193825647">"कॅलेंडर इव्हेंट जोडा किंवा बदला आणि मालकाला न कळवता अतिथींना ईमेल पाठवा"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="1675270619903625982">"हा अॅप आपल्या टॅब्लेटवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="9017809326268135866">"हा अॅप आपल्या टीव्हीवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"हा अॅप आपल्या फोनवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="1675270619903625982">"हा अॅप आपल्या टॅब्लेटवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="9017809326268135866">"हा अॅप आपल्या टीव्हीवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"हा अॅप आपल्या फोनवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अतिरिक्त स्थान प्रदाता आदेश अॅक्सेस करा"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"अॅपला अतिरिक्त स्थान प्रदाता आदेशावर प्रवेश करण्याची अनुमती देते. हे कदाचित अॅपला GPS किंवा इतर स्थान स्त्रोत च्या ऑपरेशनमध्ये हस्तक्षेप करण्याची अनुमती देऊ शकते."</string>
<string name="permlab_accessFineLocation" msgid="6265109654698562427">"फक्त फोरग्राउंडमध्ये अचूकपणे अॅक्सेस करा"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच तुमचे अचूक स्थान मिळवू शकते. या स्थान सेवा सुरू करणे आणि त्या वापरण्यासाठी अॅपसाठी तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे, यामुळे बॅटरी वापर वाढू शकतो."</string>
<string name="permlab_accessCoarseLocation" msgid="3707180371693213469">"फक्त फोरग्राउंडमध्ये अंदाजे स्थान (नेटवर्क आधारित) अॅक्सेस करा"</string>
- <string name="permdesc_accessCoarseLocation" product="tablet" msgid="8594719010575779120">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टॅबलेटवर उपलब्ध करणे आवश्यक आहे."</string>
- <string name="permdesc_accessCoarseLocation" product="tv" msgid="3027871910200890806">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टीव्हीवर उपलब्ध करणे आवश्यक आहे."</string>
- <string name="permdesc_accessCoarseLocation" product="default" msgid="854896049371048754">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. ते वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे."</string>
+ <string name="permdesc_accessCoarseLocation" product="tablet" msgid="8594719010575779120">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टॅबलेटवर उपलब्ध करणे आवश्यक आहे."</string>
+ <string name="permdesc_accessCoarseLocation" product="tv" msgid="3027871910200890806">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टीव्हीवर उपलब्ध करणे आवश्यक आहे."</string>
+ <string name="permdesc_accessCoarseLocation" product="default" msgid="854896049371048754">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. ते वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे."</string>
<string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"बॅकग्राउंडमध्ये स्थान अॅक्सेस करू शकतो"</string>
- <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"याला अंदाजे किंवा अचूक स्थान अॅक्सेस करण्यास अतिरिक्त मंजूरी दिल्यास, बॅकग्राउंडमध्ये चालतांना अॅप स्थान अॅक्सेस करू शकतो."</string>
+ <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"याला अंदाजे किंवा अचूक स्थान अॅक्सेस करण्यास अतिरिक्त मंजूरी दिल्यास, बॅकग्राउंडमध्ये चालतांना अॅप स्थान अॅक्सेस करू शकतो."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"आपल्या ऑडिओ सेटिंग्ज बदला"</string>
- <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"व्हॉल्यूम आणि आउटपुटसाठी कोणता स्पीकर वापरला आहे यासारख्या समग्र ऑडिओ सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"व्हॉल्यूम आणि आउटपुटसाठी कोणता स्पीकर वापरला आहे यासारख्या समग्र ऑडिओ सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ऑडिओ रेकॉर्ड"</string>
- <string name="permdesc_recordAudio" msgid="4245930455135321433">"हा अॅप कोणत्याही वेळी मायक्रोफोन वापरून ऑडिओ रेकॉर्ड करू शकता."</string>
+ <string name="permdesc_recordAudio" msgid="4245930455135321433">"हा अॅप कोणत्याही वेळी मायक्रोफोन वापरून ऑडिओ रेकॉर्ड करू शकता."</string>
<string name="permlab_sim_communication" msgid="2935852302216852065">"सिम वर कमांड पाठवा"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"अॅप ला सिम वर कमांड पाठविण्याची अनुमती देते. हे खूप धोकादायक असते."</string>
<string name="permlab_activityRecognition" msgid="3634590230567608356">"शारीरिक अॅक्टिव्हिटी ओळखा"</string>
- <string name="permdesc_activityRecognition" msgid="3143453925156552894">"हे अॅप तुमच्या शारीरिक अॅक्टिव्हिटी ओळखू शकते."</string>
+ <string name="permdesc_activityRecognition" msgid="3143453925156552894">"हे अॅप तुमच्या शारीरिक अॅक्टिव्हिटी ओळखू शकते."</string>
<string name="permlab_camera" msgid="3616391919559751192">"चित्रे आणि व्हिडिओ घ्या"</string>
- <string name="permdesc_camera" msgid="5392231870049240670">"हा अॅप कोणत्याही वेळी कॅमेरा वापरून चित्रेे घेऊ आणि व्हिडिअो रेकॉर्ड करू शकतो."</string>
+ <string name="permdesc_camera" msgid="5392231870049240670">"हा अॅप कोणत्याही वेळी कॅमेरा वापरून चित्रेे घेऊ आणि व्हिडिअो रेकॉर्ड करू शकतो."</string>
<string name="permlab_vibrate" msgid="7696427026057705834">"व्हायब्रेट नियंत्रित करा"</string>
- <string name="permdesc_vibrate" msgid="6284989245902300945">"अॅप ला व्हायब्रेटर नियंत्रित करण्यासाठी अनुमती देते."</string>
+ <string name="permdesc_vibrate" msgid="6284989245902300945">"अॅप ला व्हायब्रेटर नियंत्रित करण्यासाठी अनुमती देते."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"फोन नंबरवर प्रत्यक्ष कॉल करा"</string>
- <string name="permdesc_callPhone" msgid="3740797576113760827">"आपल्या हस्तक्षेपाशिवाय फोन नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देते. यामुळे अनपेक्षित शुल्क किंवा कॉल लागू शकतात. लक्षात ठेवा की हे आणीबाणीच्या नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देत नाही. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय कॉल केल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
+ <string name="permdesc_callPhone" msgid="3740797576113760827">"आपल्या हस्तक्षेपाशिवाय फोन नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देते. यामुळे अनपेक्षित शुल्क किंवा कॉल लागू शकतात. लक्षात ठेवा की हे आणीबाणीच्या नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देत नाही. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय कॉल केल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS कॉल सेवा अॅक्सेस करा"</string>
<string name="permdesc_accessImsCallService" msgid="8992884015198298775">"आपल्या हस्तक्षेपाशिवाय अॅपला कॉल करण्यासाठी IMS सेवा वापरण्याची अनुमती देते."</string>
<string name="permlab_readPhoneState" msgid="9178228524507610486">"फोन स्थिती आणि ओळख वाचा"</string>
@@ -454,59 +454,59 @@
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"टॅबलेट निष्क्रिय होण्यापासून प्रतिबंधित करा"</string>
<string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"निष्क्रिय होण्यापासून प्रतिबंध करा"</string>
<string name="permlab_wakeLock" product="default" msgid="573480187941496130">"फोन निष्क्रिय होण्यापासून प्रतिबंधित करा"</string>
- <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"टॅब्लेटला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"टॅब्लेटला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"निष्क्रिय होण्यापासून टीव्हीला प्रतिबंध करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"फोनला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"फोनला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_transmitIr" msgid="7545858504238530105">"इन्फ्रारेड प्रक्षेपण करा"</string>
<string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"अॅप ला टॅब्लेटच्या इन्फ्रारेड ट्रान्समीटरचा वापर करण्याची अनुमती देते."</string>
<string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"टीव्हीचे इन्फ्रारेड ट्रान्समीटर वापरण्यासाठी अॅपला अनुमती देते."</string>
<string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"अॅप ला फोनच्या इन्फ्रारेड ट्रान्समीटरचा वापर करण्याची अनुमती देते."</string>
<string name="permlab_setWallpaper" msgid="6627192333373465143">"वॉलपेपर सेट करा"</string>
- <string name="permdesc_setWallpaper" msgid="7373447920977624745">"सिस्टम वॉलपेपर सेट करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setWallpaper" msgid="7373447920977624745">"सिस्टम वॉलपेपर सेट करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_setWallpaperHints" msgid="3278608165977736538">"तुमचा वॉलपेपर आकार समायोजित करा"</string>
- <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"सिस्टम वॉलपेपर आकार सूचना सेट करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"सिस्टम वॉलपेपर आकार सूचना सेट करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"टाइम झोन सेट करा"</string>
- <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"टॅब्लेटचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"टॅब्लेटचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"टीव्हीचा टाईम झोन बदलण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"फोनचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"फोनचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"डिव्हाइसवरील खाती शोधा"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"टॅब्लेटद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट होऊ शकतात."</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"टॅब्लेटद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट होऊ शकतात."</string>
<string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"टीव्हीद्वारे ज्ञात खात्यांची सूची मिळविण्यासाठी अॅपला अनुमती देतो. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट असू शकतात."</string>
- <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"फोनद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट करू शकतात."</string>
+ <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"फोनद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट करू शकतात."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"नेटवर्क कनेक्शन पहा"</string>
- <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"कोणती नेटवर्क अस्तित्वात आहेत आणि कनेक्ट केलेली आहेत यासारख्या नेटवर्क कनेक्शनविषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"कोणती नेटवर्क अस्तित्वात आहेत आणि कनेक्ट केलेली आहेत यासारख्या नेटवर्क कनेक्शनविषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_createNetworkSockets" msgid="7934516631384168107">"पूर्ण नेटवर्क प्रवेश आहे"</string>
- <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"नेटवर्क सॉकेट तयार करण्यासाठी आणि कस्टम नेटवर्क प्रोटोकॉल वापरण्यासाठी अॅप ला अनुमती देते. ब्राउझर आणि अन्य अॅप्लिकेशन म्हणजे इंटरनेटवर डेटा पाठवण्याचा मार्ग, म्हणजे इंटरनेटवर डेटा पाठविण्यासाठी परवानगीची आवश्यकता नसते."</string>
+ <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"नेटवर्क सॉकेट तयार करण्यासाठी आणि कस्टम नेटवर्क प्रोटोकॉल वापरण्यासाठी अॅप ला अनुमती देते. ब्राउझर आणि अन्य अॅप्लिकेशन म्हणजे इंटरनेटवर डेटा पाठवण्याचा मार्ग, म्हणजे इंटरनेटवर डेटा पाठविण्यासाठी परवानगीची आवश्यकता नसते."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"नेटवर्क कनेक्टिव्हिटी बदला"</string>
- <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"टिथर केलेली कनेक्टिव्हिटी बदला"</string>
- <string name="permdesc_changeTetherState" msgid="1524441344412319780">"टेदर केलेल्या नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeTetherState" msgid="1524441344412319780">"टेदर केलेल्या नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"वाय-फाय कनेक्शन पहा"</string>
- <string name="permdesc_accessWifiState" msgid="5002798077387803726">"वाय-फाय सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या वाय-फाय डीव्हाइसचे नाव यासारख्या, वाय-फाय नेटवर्किंग विषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_accessWifiState" msgid="5002798077387803726">"वाय-फाय सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या वाय-फाय डीव्हाइसचे नाव यासारख्या, वाय-फाय नेटवर्किंग विषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"वाय-फाय वरून कनेक्ट करा आणि डिस्कनेक्ट करा"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"वाय-फाय अॅक्सेस बिंदूंवर कनेक्ट करण्यासाठी आणि त्यावरून डिस्कनेक्ट करण्यासाठी आणि वाय-फाय नेटवर्कसाठी डिव्हाइस कॉंफिगरेशनमध्ये बदल करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"वाय-फाय मल्टिकास्ट रिसेप्शनला अनुमती द्या"</string>
- <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या टॅब्लेटवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या टॅब्लेटवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"केवळ तुमचा टीव्ही न वापरता, एकाधिक पत्ते वापरून एका वाय-फाय नेटवकवरील सर्व डीव्हाइसवर पाठविलेली पॅकेट प्राप्त करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या फोनवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
+ <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या फोनवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"ब्लूटूथ सेटिंग्ज अॅक्सेस करा"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"स्थानिक ब्लूटूथ टॅबलेट कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"स्थानिक ब्लूटूथ टॅबलेट कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"स्थानिक ब्लूटूथ टीव्ही कॉंफिगर करण्यासाठी आणि दूरस्थ डीव्हाइससह शोधण्यासाठी आणि जोडण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"स्थानिक ब्लूटूथ फोन कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"स्थानिक ब्लूटूथ फोन कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX कनेक्ट करा आणि त्यावरून डिस्कनेक्ट करा"</string>
- <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या कोणत्याही WiMAX नेटवर्क विषयीची माहिती निर्धारित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या कोणत्याही WiMAX नेटवर्क विषयीची माहिती निर्धारित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX स्थिती बदला"</string>
- <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX नेटवर्कवर टॅबलेट कनेक्ट करण्यास आणि त्यावरून टॅबलेट डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX नेटवर्कवर टॅबलेट कनेक्ट करण्यास आणि त्यावरून टॅबलेट डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"WiMAX नेटवर्कवरून टीव्ही कनेक्ट करण्यासाठी आणि त्यावरून टीव्ही डिस्कनेक्ट करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX नेटवर्कवर फोन कनेक्ट करण्यास आणि त्यावरून फोन डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX नेटवर्कवर फोन कनेक्ट करण्यास आणि त्यावरून फोन डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"ब्लूटूथ डीव्हाइससह जोडा"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"टॅबलेटवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"टॅबलेटवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अॅप ला अनुमती देते."</string>
<string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"टीव्हीवर ब्लूटूथचे कॉंफिगरेशन पाहण्यासाठी आणि जोडलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"फोनवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"फोनवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अॅप ला अनुमती देते."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"फील्ड जवळील कम्युनिकेशन नियंत्रित करा"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"फील्ड जवळील कम्युनिकेशन (NFC) टॅग, कार्डे आणि वाचक यांच्यासह संवाद करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"तुमचे स्क्रीन लॉक अक्षम करा"</string>
- <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
+ <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमेट्रिक हार्डवेअर वापरा"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ऑथेंटिकेशनसाठी बायोमेट्रिक हार्डवेअरचा वापर करण्याची अॅपला अनुमती देते"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"फिंगरप्रिंट हार्डवेअर व्यवस्थापित करा"</string>
@@ -591,11 +591,11 @@
</string-array>
<string name="face_icon_content_description" msgid="4024817159806482191">"चेहरा आयकन"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"सिंक सेटिंग्ज वाचा"</string>
- <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"खात्याच्या सिंक सेटिंग्ज वाचण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांचा अॅप संकालित केला आहे किंवा नाही हे निर्धारित करू शकते."</string>
+ <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"खात्याच्या सिंक सेटिंग्ज वाचण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांचा अॅप संकालित केला आहे किंवा नाही हे निर्धारित करू शकते."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"सिंक चालू आणि बंद करा टॉगल करा"</string>
- <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"खात्यासाठी सिंक सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांच्या अॅप चे सिंक सक्षम करण्यासाठी वापरले जाऊ शकते."</string>
+ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"खात्यासाठी सिंक सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांच्या अॅप चे सिंक सक्षम करण्यासाठी वापरले जाऊ शकते."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"सिंक आकडेवारी वाचा"</string>
- <string name="permdesc_readSyncStats" msgid="1510143761757606156">"सिंक इव्हेंटचा इतिहास आणि किती डेटाचे सिंक केले आहे यासह, खात्याची सिंक स्थिती वाचण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_readSyncStats" msgid="1510143761757606156">"सिंक इव्हेंटचा इतिहास आणि किती डेटाचे सिंक केले आहे यासह, खात्याची सिंक स्थिती वाचण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_sdcardRead" msgid="1438933556581438863">"तुमच्या शेअर केलेल्या स्टोरेजचे आशय वाचते"</string>
<string name="permdesc_sdcardRead" msgid="1804941689051236391">"अॅपला तुमच्या शेअर केलेल्या स्टोरेजचे आशय वाचण्याची अनुमती देते."</string>
<string name="permlab_sdcardWrite" msgid="9220937740184960897">"तुमच्या शेअर केलेल्या स्टोरेजच्या आशयांमध्ये सुधारणा करा किंवा हटवा"</string>
@@ -607,17 +607,17 @@
<string name="permlab_register_call_provider" msgid="108102120289029841">"नवीन टेलिकॉम कनेक्शनची नोंदणी करा"</string>
<string name="permdesc_register_call_provider" msgid="7034310263521081388">"नवीन टेलिकॉम कनेक्शनची नोंदणी करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_connection_manager" msgid="1116193254522105375">"टेलिकॉम कनेक्शन व्यवस्थापित करा"</string>
- <string name="permdesc_connection_manager" msgid="5925480810356483565">"टेलिकॉम कनेक्शन व्यवस्थापित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_connection_manager" msgid="5925480810356483565">"टेलिकॉम कनेक्शन व्यवस्थापित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_bind_incall_service" msgid="6773648341975287125">"कॉल-मधील स्क्रीनशी परस्परसंवाद करा"</string>
<string name="permdesc_bind_incall_service" msgid="8343471381323215005">"वापरकर्ता कॉल-मधील स्क्रीन केव्हा आणि कशी पाहतो ते नियंत्रित करण्याची अॅपला अनुमती देते."</string>
<string name="permlab_bind_connection_service" msgid="3557341439297014940">"टेलिफोनी सेवांशी परस्परसंवाद साधा"</string>
- <string name="permdesc_bind_connection_service" msgid="4008754499822478114">"कॉल करण्यासाठी/घेण्यासाठी टेलिफोनी सेवांशी परस्परसंवाद साधण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bind_connection_service" msgid="4008754499822478114">"कॉल करण्यासाठी/घेण्यासाठी टेलिफोनी सेवांशी परस्परसंवाद साधण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_control_incall_experience" msgid="9061024437607777619">"एक कॉल-मधील वापरकर्ता अनुभव प्रदान करा"</string>
<string name="permdesc_control_incall_experience" msgid="915159066039828124">"अॅप्सला कॉल-मधील वापरकर्ता अनुभव प्रदान करण्याची अनुमती देते."</string>
<string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"ऐतिहासिक नेटवर्क वापर वाचा"</string>
<string name="permdesc_readNetworkUsageHistory" msgid="7689060749819126472">"विशिष्ट नेटवर्क आणि अॅप्सकरिता ऐतिहासिक नेटवर्क वापराचे वाचन करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_manageNetworkPolicy" msgid="2562053592339859990">"नेटवर्क धोरण व्यवस्थापित करा"</string>
- <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"नेटवर्क धोरणे व्यवस्थापित करण्यासाठी आणि अॅप-विशिष्ट नियम परिभाषित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"नेटवर्क धोरणे व्यवस्थापित करण्यासाठी आणि अॅप-विशिष्ट नियम परिभाषित करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"नेटवर्क वापर हिशोब सुधारित करा"</string>
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"अॅप्स वर नेटवर्क वापराचा हिशोब कसा घेतला जातो हे सुधारित करण्यासाठी अॅप्स ला अनुमती देते. सामान्य अॅप्सद्वारे वापरण्यासाठी नाही."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"प्रवेश सूचना"</string>
@@ -633,7 +633,7 @@
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"नेटवर्क स्थितींवरील निरीक्षणांसाठी ऐका"</string>
<string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"अनु्प्रयोगाला नेटवर्क स्थितींवरील निरीक्षणे ऐकण्यासाठी अनुमती देते. सामान्य अॅप्ससाठी कधीही आवश्यक नसावे."</string>
<string name="permlab_setInputCalibration" msgid="4902620118878467615">"इनपुट डिव्हाइस कॅलिब्रेशन बदला"</string>
- <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"स्पर्श स्क्रीनची कॅलिब्रेशन प्राचले सुधारित करण्यासाठी अॅप ला अनुमती देते. सामान्य अॅप्स साठी कधीही आवश्यक नसते."</string>
+ <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"स्पर्श स्क्रीनची कॅलिब्रेशन प्राचले सुधारित करण्यासाठी अॅप ला अनुमती देते. सामान्य अॅप्स साठी कधीही आवश्यक नसते."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM प्रमाणपत्रे अॅक्सेस करा"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM प्रमाणपत्रांची तरतूद करण्यासाठी आणि वापरण्यासाठी अनुप्रयोगास अनुमती देते. सामान्य अॅप्सकरिता कधीही आवश्यकता नसते."</string>
<string name="permlab_handoverStatus" msgid="7820353257219300883">"Android बीम स्थानांतरण स्थिती प्राप्त करा"</string>
@@ -672,7 +672,7 @@
<string name="policylab_expirePassword" msgid="5610055012328825874">"स्क्रीन लॉक पासवर्ड कालबाह्यता सेट करा"</string>
<string name="policydesc_expirePassword" msgid="5367525762204416046">"लॉक-स्क्रीन पासवर्ड किती वारंवार बदलणे आवश्यक आहे ते बदला."</string>
<string name="policylab_encryptedStorage" msgid="8901326199909132915">"स्टोरेज एंक्रिप्शन सेट करा"</string>
- <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"स्टोअर केलेला अॅप डेटा एंक्रिप्ट केला जाणे आवश्यक आहे."</string>
+ <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"स्टोअर केलेला अॅप डेटा एंक्रिप्ट केला जाणे आवश्यक आहे."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"कॅमेरे अक्षम करा"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"सर्व डिव्हाइस कॅमेर्यांचा वापर प्रतिबंधित करा."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"काही स्क्रीन लॉक वैशिष्ट्ये अक्षम करा"</string>
@@ -897,7 +897,7 @@
<string name="granularity_label_link" msgid="5815508880782488267">"लिंक"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"रेखा"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"फॅक्टरी चाचणी अयशस्वी"</string>
- <string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST क्रिया फक्त /सिस्टम/अॅप मध्ये इंस्टॉल केलेल्या पॅकेजसाठी समर्थित आहे."</string>
+ <string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST क्रिया फक्त /सिस्टम/अॅप मध्ये इंस्टॉल केलेल्या पॅकेजसाठी समर्थित आहे."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST क्रिया प्रदान करणारे कोणतेही पॅकेज आढळले नाही."</string>
<string name="factorytest_reboot" msgid="6320168203050791643">"रीबूट करा"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"\"<xliff:g id="TITLE">%s</xliff:g>\" वरील पृष्ठ हे म्हणते:"</string>
@@ -928,17 +928,17 @@
<string name="autofill_area" msgid="3547409050889952423">"क्षेत्र"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"अमिरात"</string>
<string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"तुमचे वेब बुकमार्क आणि इतिहास वाचा"</string>
- <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"ब्राउझरने भेट दिलेल्या सर्व URL चा इतिहास आणि ब्राउझरचे सर्व बुकमार्क वाचण्यास अॅप ला अनुमती देते. टीप: या परवानगीची तृतीय-पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमता असलेल्या अन्य अनुप्रयोगांद्वारे अंमलबजावणी करू शकत नाही."</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"ब्राउझरने भेट दिलेल्या सर्व URL चा इतिहास आणि ब्राउझरचे सर्व बुकमार्क वाचण्यास अॅप ला अनुमती देते. टीप: या परवानगीची तृतीय-पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमता असलेल्या अन्य अनुप्रयोगांद्वारे अंमलबजावणी करू शकत नाही."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"वेब बुकमार्क आणि इतिहास लिहा"</string>
- <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"तुमच्या टॅब्लेटवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"तुमच्या टॅब्लेटवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"तुमच्या टीव्हीवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅपला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅपला अनुमती देऊ शकते. टीप: या परवानगीची अंमलबजावणी वेब ब्राउझिंग क्षमता असलेल्या तृतीय-पक्ष ब्राउझरद्वारे किंवा इतर अॅप्लिकेशनद्वारे केली जाऊ शकत नाही."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"तुमच्या फोनवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"तुमच्या फोनवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"अलार्म सेट करा"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"इंस्टॉल केलेल्या अलार्म घड्याळ अॅपमध्ये अलार्म सेट करण्यासाठी अॅपला अनुमती देते. काही अलार्म घड्याळ अॅप्समध्ये हे वैशिष्ट्य नसू शकते."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"व्हॉइसमेल जोडा"</string>
- <string name="permdesc_addVoicemail" msgid="6604508651428252437">"आपल्या व्हॉइसमेल इनबॉक्समध्ये मेसेज जोडण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_addVoicemail" msgid="6604508651428252437">"आपल्या व्हॉइसमेल इनबॉक्समध्ये मेसेज जोडण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ब्राउझर भौगोलिक स्थान परवानग्या सुधारित करा"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ब्राउझरच्या भौगोलिक स्थान परवानग्या सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स यादृच्छिक वेबसाइटवर स्थान माहिती पाठविण्यास अनुमती देण्यासाठी याचा वापर करू शकतात."</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ब्राउझरच्या भौगोलिक स्थान परवानग्या सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स यादृच्छिक वेबसाइटवर स्थान माहिती पाठविण्यास अनुमती देण्यासाठी याचा वापर करू शकतात."</string>
<string name="save_password_message" msgid="767344687139195790">"ब्राउझरने हा पासवर्ड लक्षात ठेवावा असे तुम्ही इच्छिता?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"आत्ता नाही"</string>
<string name="save_password_remember" msgid="6491879678996749466">"लक्षात ठेवा"</string>
@@ -1083,25 +1083,25 @@
<string name="deleteText" msgid="6979668428458199034">"हटवा"</string>
<string name="inputMethod" msgid="1653630062304567879">"इनपुट पद्धत"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"मजकूर क्रिया"</string>
- <string name="email" msgid="4560673117055050403">"ईमेल"</string>
+ <string name="email" msgid="4560673117055050403">"ईमेल करा"</string>
<string name="email_desc" msgid="3638665569546416795">"निवडलेल्या अॅड्रेसवर ईमेल करा"</string>
<string name="dial" msgid="1253998302767701559">"कॉल करा"</string>
<string name="dial_desc" msgid="6573723404985517250">"निवडलेल्या फोन नंबरवर कॉल करा"</string>
- <string name="map" msgid="5441053548030107189">"नकाशा"</string>
+ <string name="map" msgid="5441053548030107189">"नकाशा उघडा"</string>
<string name="map_desc" msgid="1836995341943772348">"निवडलेला पत्ता शोधा"</string>
<string name="browse" msgid="1245903488306147205">"उघडा"</string>
<string name="browse_desc" msgid="8220976549618935044">"निवडलेली URL उघडा"</string>
- <string name="sms" msgid="4560537514610063430">"मेसेज"</string>
+ <string name="sms" msgid="4560537514610063430">"मेसेज करा"</string>
<string name="sms_desc" msgid="7526588350969638809">"निवडलेल्या फोन नंबरवर एसएमएस करा"</string>
<string name="add_contact" msgid="7867066569670597203">"जोडा"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"संपर्कांमध्ये जोडा"</string>
- <string name="view_calendar" msgid="979609872939597838">"पहा"</string>
+ <string name="view_calendar" msgid="979609872939597838">"पाहा"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"निवडलेली वेळ कॅलेंडरमध्ये पाहा"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"शेड्यूल"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"निवडलेल्या वेळेसाठी इव्हेंट शेड्यूल करा"</string>
- <string name="view_flight" msgid="7691640491425680214">"ट्रॅक"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"शेड्युल करा"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"निवडलेल्या वेळेसाठी इव्हेंट शेड्युल करा"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ट्रॅक करा"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"निवडलेले विमान ट्रॅक करा"</string>
- <string name="translate" msgid="9218619809342576858">"भाषांतर"</string>
+ <string name="translate" msgid="9218619809342576858">"भाषांतर करा"</string>
<string name="translate_desc" msgid="4502367770068777202">"निवडलेल्या मजकुराचे भाषांतर करा"</string>
<string name="define" msgid="7394820043869954211">"व्याख्या सांगा"</string>
<string name="define_desc" msgid="7910883642444919726">"निवडलेल्या मजकुराची व्याख्या सांगा"</string>
@@ -1143,18 +1143,18 @@
<string name="use_a_different_app" msgid="8134926230585710243">"एक भिन्न अॅप वापरा"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"डाउनलोड केलेल्या सिस्टम सेटिंग्ज > Apps > मधील डीफॉल्ट साफ करा."</string>
<string name="chooseActivity" msgid="7486876147751803333">"क्रिया निवडा"</string>
- <string name="chooseUsbActivity" msgid="6894748416073583509">"USB डिव्हाइससाठी अॅप निवडा"</string>
+ <string name="chooseUsbActivity" msgid="6894748416073583509">"USB डिव्हाइससाठी अॅप निवडा"</string>
<string name="noApplications" msgid="2991814273936504689">"कोणतेही अॅप्स ही क्रिया करू शकत नाहीत."</string>
<string name="aerr_application" msgid="250320989337856518">"<xliff:g id="APPLICATION">%1$s</xliff:g> थांबला आहे"</string>
<string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> थांबली आहे"</string>
<string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> थांबतो"</string>
<string name="aerr_process_repeated" msgid="6235302956890402259">"<xliff:g id="PROCESS">%1$s</xliff:g> थांबते"</string>
- <string name="aerr_restart" msgid="7581308074153624475">"अॅप पुन्हा उघडा"</string>
+ <string name="aerr_restart" msgid="7581308074153624475">"अॅप पुन्हा उघडा"</string>
<string name="aerr_report" msgid="5371800241488400617">"अभिप्राय पाठवा"</string>
<string name="aerr_close" msgid="2991640326563991340">"बंद करा"</string>
<string name="aerr_mute" msgid="1974781923723235953">"डिव्हाइस रीस्टार्ट होईपर्यंत म्यूट करा"</string>
<string name="aerr_wait" msgid="3199956902437040261">"प्रतीक्षा करा"</string>
- <string name="aerr_close_app" msgid="3269334853724920302">"अॅप बंद करा"</string>
+ <string name="aerr_close_app" msgid="3269334853724920302">"अॅप बंद करा"</string>
<string name="anr_title" msgid="4351948481459135709"></string>
<string name="anr_activity_application" msgid="8493290105678066167">"<xliff:g id="APPLICATION">%2$s</xliff:g> प्रतिसाद देत नाही"</string>
<string name="anr_activity_process" msgid="1622382268908620314">"<xliff:g id="ACTIVITY">%1$s</xliff:g> प्रतिसाद देत नाही"</string>
@@ -1164,7 +1164,7 @@
<string name="report" msgid="4060218260984795706">"अहवाल द्या"</string>
<string name="wait" msgid="7147118217226317732">"प्रतीक्षा करा"</string>
<string name="webpage_unresponsive" msgid="3272758351138122503">"पृष्ठ प्रतिसाद न देणारे झाले आहे.\n\nतुम्ही हे बंद करू इच्छिता?"</string>
- <string name="launch_warning_title" msgid="1547997780506713581">"अॅप पुनर्निर्देशित केला"</string>
+ <string name="launch_warning_title" msgid="1547997780506713581">"अॅप पुनर्निर्देशित केला"</string>
<string name="launch_warning_replace" msgid="6202498949970281412">"<xliff:g id="APP_NAME">%1$s</xliff:g> आता चालत आहे."</string>
<string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g> मूळतः लाँच केले."</string>
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"स्केल"</string>
@@ -1175,7 +1175,7 @@
<string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे Android OS च्या विसंगत आवृत्तीसाठी तयार केले होते आणि ते अनपेक्षित पद्धतीने काम करू शकते. अॅपची अपडेट केलेली आवृत्ती उपलब्ध असू शकते."</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"नेहमी दर्शवा"</string>
<string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"अपडेट आहे का ते तपासा"</string>
- <string name="smv_application" msgid="3307209192155442829">"अॅप <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने तिच्या स्वयं-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले आहे."</string>
+ <string name="smv_application" msgid="3307209192155442829">"अॅप <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने तिच्या स्वयं-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले आहे."</string>
<string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> प्रक्रियेने तिच्या स्वतः-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले."</string>
<string name="android_upgrading_title" product="default" msgid="7513829952443484438">"फोन अपडेट होत आहे…"</string>
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"टॅबलेट अपडेट होत आहे…"</string>
@@ -1186,7 +1186,7 @@
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"संचयन ऑप्टिमाइझ करत आहे."</string>
<string name="android_upgrading_notification_title" product="default" msgid="1511552415039349062">"सिस्टम अपडेट संपत आहे…"</string>
<string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> श्रेणीसुधारित करत आहे…"</string>
- <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप ऑप्टिमाइझ करत आहे."</string>
+ <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप ऑप्टिमाइझ करत आहे."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> तयार करत आहे."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"अॅप्स प्रारंभ करत आहे."</string>
<string name="android_upgrading_complete" msgid="1405954754112999229">"बूट समाप्त होत आहे."</string>
@@ -1288,7 +1288,7 @@
<string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> वर फोन कनेक्ट केलेला असताना तो वाय-फाय वरून तात्पुरता डिस्कनेक्ट केला जाईल"</string>
<string name="select_character" msgid="3365550120617701745">"वर्ण घाला"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMS मेसेज पाठवत आहे"</string>
- <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> मोठ्या संख्येने SMS मेसेज पाठवत आहे. तुम्ही या अॅप ला मेसेज पाठविणे सुरु ठेवण्याची अनुमती देऊ इच्छिता?"</string>
+ <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> मोठ्या संख्येने SMS मेसेज पाठवत आहे. तुम्ही या अॅप ला मेसेज पाठविणे सुरु ठेवण्याची अनुमती देऊ इच्छिता?"</string>
<string name="sms_control_yes" msgid="3663725993855816807">"अनुमती द्या"</string>
<string name="sms_control_no" msgid="625438561395534982">"नकार द्या"</string>
<string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> हा <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>वर एक मेसेज पाठवू इच्छितो."</string>
@@ -1445,7 +1445,7 @@
<string name="no_file_chosen" msgid="6363648562170759465">"फाईल निवडली नाही"</string>
<string name="reset" msgid="2448168080964209908">"रीसेट करा"</string>
<string name="submit" msgid="1602335572089911941">"सबमिट करा"</string>
- <string name="car_mode_disable_notification_title" msgid="5704265646471239078">"ड्रायव्हिंग अॅप चालू आहे"</string>
+ <string name="car_mode_disable_notification_title" msgid="5704265646471239078">"ड्रायव्हिंग अॅप चालू आहे"</string>
<string name="car_mode_disable_notification_message" msgid="7647248420931129377">"ड्रायव्हिंग अॅपमधून बाहेर पाडण्यासाठी टॅप करा."</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग किंवा हॉटस्पॉट सक्रिय"</string>
<string name="tethered_notification_message" msgid="2113628520792055377">"सेट करण्यासाठी टॅप करा."</string>
@@ -1774,7 +1774,7 @@
<string name="package_deleted_device_owner" msgid="2307122077550236438">"आपल्या प्रशासकाने हटवले"</string>
<string name="battery_saver_description_with_learn_more" msgid="6323937147992667707">"बॅटरी लाइफ वाढवण्यासाठी, बॅटरी सेव्हर काही डिव्हाइस वैशिष्ट्ये बंद करते आणि अॅप्सना प्रतिबंधित करते. "<annotation id="url">"अधिक जाणून घ्या"</annotation></string>
<string name="battery_saver_description" msgid="769989536172631582">"बॅटरी लाइफ वाढवण्यासाठी, बॅटरी सेव्हर काही वैशिष्ट्ये बंद करते आणि अॅप्स प्रतिबंधित करते."</string>
- <string name="data_saver_description" msgid="6015391409098303235">"डेटा वापर कमी करण्यात मदत करण्यासाठी, डेटा सर्व्हर काही अॅप्सना पार्श्वभूमीमध्ये डेटा पाठविण्यास किंवा प्राप्त करण्यास प्रतिबंधित करतो. तुम्ही सध्या वापरत असलेला अॅप डेटामध्ये प्रवेश करू शकतो परंतु तसे तो खूप कमी वेळा करू शकतो. याचा अर्थ, उदाहरणार्थ, तुम्ही इमेज टॅप करेपर्यंत त्या प्रदर्शित करणार नाहीत असा असू शकतो."</string>
+ <string name="data_saver_description" msgid="6015391409098303235">"डेटा वापर कमी करण्यात मदत करण्यासाठी, डेटा सर्व्हर काही अॅप्सना पार्श्वभूमीमध्ये डेटा पाठविण्यास किंवा प्राप्त करण्यास प्रतिबंधित करतो. तुम्ही सध्या वापरत असलेला अॅप डेटामध्ये प्रवेश करू शकतो परंतु तसे तो खूप कमी वेळा करू शकतो. याचा अर्थ, उदाहरणार्थ, तुम्ही इमेज टॅप करेपर्यंत त्या प्रदर्शित करणार नाहीत असा असू शकतो."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"डेटा बचतकर्ता चालू करायचा?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"चालू करा"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
@@ -1861,16 +1861,16 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"सर्व भाषा"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"सर्व प्रदेश"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"शोध"</string>
- <string name="app_suspended_title" msgid="2075071241147969611">"अॅप उपलब्ध नाही"</string>
+ <string name="app_suspended_title" msgid="2075071241147969611">"अॅप उपलब्ध नाही"</string>
<string name="app_suspended_default_message" msgid="123166680425711887">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> आत्ता उपलब्ध नाही. हे <xliff:g id="APP_NAME_1">%2$s</xliff:g> कडून व्यवस्थापित केले जाते."</string>
<string name="app_suspended_more_details" msgid="1131804827776778187">"अधिक जाणून घ्या"</string>
<string name="work_mode_off_title" msgid="1118691887588435530">"कार्य प्रोफाइल चालू ठेवायची?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"तुमची कार्य अॅप्स, सूचना, डेटा आणि अन्य कार्य प्रोफाइल वैशिष्ट्ये चालू केली जातील"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"चालू करा"</string>
- <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"हे अॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पाहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"हे अॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पाहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
<string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"अपडेट आहे का ते तपासा"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"आपल्याकडे नवीन मेसेज आहेत"</string>
- <string name="new_sms_notification_content" msgid="7002938807812083463">"पाहण्यासाठी SMS अॅप उघडा"</string>
+ <string name="new_sms_notification_content" msgid="7002938807812083463">"पाहण्यासाठी SMS अॅप उघडा"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"काही कार्यक्षमता मर्यादित असू शकतात"</string>
<string name="user_encrypted_message" msgid="4923292604515744267">"अनलॉक करण्यासाठी टॅप करा"</string>
<string name="user_encrypted_detail" msgid="5708447464349420392">"वापरकर्ता डेटा लॉक केला"</string>
@@ -1880,7 +1880,7 @@
<string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"फायली पाहण्यासाठी टॅप करा"</string>
<string name="pin_target" msgid="3052256031352291362">"पिन"</string>
<string name="unpin_target" msgid="3556545602439143442">"अनपिन करा"</string>
- <string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
+ <string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="5268556852031489931">"डेमो प्रारंभ करत आहे..."</string>
<string name="demo_restarting_message" msgid="952118052531642451">"डिव्हाइस रीसेट करत आहे..."</string>
@@ -1945,13 +1945,13 @@
<string name="popup_window_default_title" msgid="4874318849712115433">"पॉपअप विंडो"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="4860853725206702336">"अॅपची आवृत्ती डाउनग्रेड केली, किंवा ती या शॉर्टकटशी कंपॅटिबल नाही"</string>
- <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अॅप बॅकअप आणि रिस्टोअर करण्यास सपोर्ट देत नसल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
- <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अॅप स्वाक्षरी न जुळल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
+ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अॅप बॅकअप आणि रिस्टोअर करण्यास सपोर्ट देत नसल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अॅप स्वाक्षरी न जुळल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"शॉर्टकट बंद केलेला आहे"</string>
<string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"अनइंस्टॉल करा"</string>
<string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"तरीही उघडा"</string>
- <string name="harmful_app_warning_title" msgid="8982527462829423432">"हानिकारक अॅप आढळला"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"हानिकारक अॅप आढळला"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> ला <xliff:g id="APP_2">%2$s</xliff:g> चे तुकडे दाखवायचे आहेत"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"संपादित करा"</string>
<string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"कॉल आणि सूचनांवर व्हायब्रेट होईल"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 39afd74..a3da75e 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1083,24 +1083,24 @@
<string name="deleteText" msgid="6979668428458199034">"ဖျက်ရန်"</string>
<string name="inputMethod" msgid="1653630062304567879">"ထည့်သွင်းရန်နည်းလမ်း"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"စာတို လုပ်ဆောင်ချက်"</string>
- <string name="email" msgid="4560673117055050403">"အီးမေးလ်"</string>
+ <string name="email" msgid="4560673117055050403">"အီးမေးလ်ပို့ရန်"</string>
<string name="email_desc" msgid="3638665569546416795">"ရွေးထားသည့် လိပ်စာသို့ အီးမေးလ်ပို့ရန်"</string>
<string name="dial" msgid="1253998302767701559">"ခေါ်ဆိုရန်"</string>
<string name="dial_desc" msgid="6573723404985517250">"ရွေးထားသည့် ဖုန်းနံပါတ်ကို ခေါ်ရန်"</string>
- <string name="map" msgid="5441053548030107189">"မြေပုံ"</string>
+ <string name="map" msgid="5441053548030107189">"မြေပုံဖွင့်ရန်"</string>
<string name="map_desc" msgid="1836995341943772348">"ရွေးထားသည့် လိပ်စာကို ရှာဖွေရန်"</string>
<string name="browse" msgid="1245903488306147205">"ဖွင့်ရန်"</string>
<string name="browse_desc" msgid="8220976549618935044">"ရွေးထားသည့် URL ကို ဖွင့်ရန်"</string>
- <string name="sms" msgid="4560537514610063430">"SMS"</string>
+ <string name="sms" msgid="4560537514610063430">"SMS ပို့ရန်"</string>
<string name="sms_desc" msgid="7526588350969638809">"ရွေးထားသည့် ဖုန်းနံပါတ်ကို မက်ဆေ့ဂျ်ပို့ရန်"</string>
<string name="add_contact" msgid="7867066569670597203">"ထည့်ရန်"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"အဆက်အသွယ်များသို့ ထည့်ရန်"</string>
<string name="view_calendar" msgid="979609872939597838">"ကြည့်ရန်"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"ရွေးထားသည့် အချိန်ကို ပြက္ခဒိန်တွင် ကြည့်ရန်"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"အချိန်ဇယား"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"အစီအစဉ်ထည့်ရန်"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ရွေးထားသည့်အချိန်အတွက် အစီအစဉ်ပြုလုပ်ရန်"</string>
<string name="view_flight" msgid="7691640491425680214">"ရှာရန်"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"ရွေးထားသည့် လေယာဉ်ခရီးစဉ်ကို ရှာဖွေရန်"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"ရွေးထားသည့် လေယာဉ်ခရီးစဉ်ကို ရှာရန်"</string>
<string name="translate" msgid="9218619809342576858">"ဘာသာပြန်ရန်"</string>
<string name="translate_desc" msgid="4502367770068777202">"ရွေးထားသောစာသားကို ဘာသာပြန်ရန်"</string>
<string name="define" msgid="7394820043869954211">"အဓိပ္ပာယ်ဖွင့်ဆိုရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index f606d77..30cd600 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1265,7 +1265,7 @@
</string-array>
<string name="network_switch_type_name_unknown" msgid="4552612897806660656">"en ukjent nettverkstype"</string>
<string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kan ikke koble til Wi-Fi"</string>
- <string name="wifi_watchdog_network_disabled_detailed" msgid="4917472096696322767">" har en dårlig Internett-tilkobling."</string>
+ <string name="wifi_watchdog_network_disabled_detailed" msgid="4917472096696322767">" har en dårlig internettilkobling."</string>
<string name="wifi_connect_alert_title" msgid="8455846016001810172">"Vil du tillat tilkoblingen?"</string>
<string name="wifi_connect_alert_message" msgid="6451273376815958922">"Appen %1$s vil koble til Wi-Fi-nettverket %2$s"</string>
<string name="wifi_connect_default_application" msgid="7143109390475484319">"En app"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index a6bd007..2ff9fde 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -247,7 +247,7 @@
<string name="global_action_settings" msgid="1756531602592545966">"Instellingen"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Helpen"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Spraakassistent"</string>
- <string name="global_action_lockdown" msgid="1099326950891078929">"Afgesloten"</string>
+ <string name="global_action_lockdown" msgid="1099326950891078929">"Lockdown"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999 +"</string>
<string name="notification_hidden_text" msgid="6351207030447943784">"Nieuwe melding"</string>
<string name="notification_channel_virtual_keyboard" msgid="6969925135507955575">"Virtueel toetsenbord"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 1d1f3e7..e3fe527 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1083,7 +1083,7 @@
<string name="deleteText" msgid="6979668428458199034">"ਮਿਟਾਓ"</string>
<string name="inputMethod" msgid="1653630062304567879">"ਇਨਪੁੱਟ ਵਿਧੀ"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"ਟੈਕਸਟ ਕਿਰਿਆਵਾਂ"</string>
- <string name="email" msgid="4560673117055050403">"ਈਮੇਲ ਕਰੋ"</string>
+ <string name="email" msgid="4560673117055050403">"ਈਮੇਲ ਖੋਲ੍ਹੋ"</string>
<string name="email_desc" msgid="3638665569546416795">"ਚੁਣੇ ਹੋਏ ਪਤੇ \'ਤੇ ਈਮੇਲ ਭੇਜੋ"</string>
<string name="dial" msgid="1253998302767701559">"ਕਾਲ ਕਰੋ"</string>
<string name="dial_desc" msgid="6573723404985517250">"ਚੁਣੇ ਗਏ ਫ਼ੋਨ ਨੰਬਰ \'ਤੇ ਕਾਲ ਕਰੋ"</string>
@@ -1097,11 +1097,11 @@
<string name="add_contact_desc" msgid="4830217847004590345">"ਸੰਪਰਕਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="view_calendar" msgid="979609872939597838">"ਦੇਖੋ"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"ਕੈਲੰਡਰ ਵਿੱਚ ਚੁਣਿਆ ਗਿਆ ਸਮਾਂ ਦੇਖੋ"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"ਸਮਾਂ-ਸੂਚੀ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ਸਮਾਂ-ਸੂਚੀ ਬਣਾਓ"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ਚੁਣੇ ਗਏ ਸਮੇਂ ਲਈ ਇਵੈਂਟ ਦੀ ਸਮਾਂ-ਸੂਚੀ ਬਣਾਓ"</string>
<string name="view_flight" msgid="7691640491425680214">"ਟਰੈਕ ਕਰੋ"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ਚੁਣੀ ਗਈ ਉਡਾਣ ਨੂੰ ਟਰੈਕ ਕਰੋ"</string>
- <string name="translate" msgid="9218619809342576858">"ਅਨੁਵਾਦ"</string>
+ <string name="translate" msgid="9218619809342576858">"ਅਨੁਵਾਦ ਕਰੋ"</string>
<string name="translate_desc" msgid="4502367770068777202">"ਚੋਣਵੀਂ ਲਿਖਤ ਦਾ ਅਨੁਵਾਦ ਕਰੋ"</string>
<string name="define" msgid="7394820043869954211">"ਪਰਿਭਾਸ਼ਾ ਦਿਓ"</string>
<string name="define_desc" msgid="7910883642444919726">"ਚੋਣਵੀਂ ਲਿਖਤ ਦਾ ਅਨੁਵਾਦ ਕਰੋ"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9a70f0b..0790e90 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1123,26 +1123,26 @@
<string name="deleteText" msgid="6979668428458199034">"Izbriši"</string>
<string name="inputMethod" msgid="1653630062304567879">"Način vnosa"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Besedilna dejanja"</string>
- <string name="email" msgid="4560673117055050403">"E-pošta"</string>
+ <string name="email" msgid="4560673117055050403">"Pošlji e-pošto"</string>
<string name="email_desc" msgid="3638665569546416795">"Pošlji na izbrani naslov"</string>
<string name="dial" msgid="1253998302767701559">"Pokliči"</string>
<string name="dial_desc" msgid="6573723404985517250">"Pokliči izbrano telefonsko številko"</string>
- <string name="map" msgid="5441053548030107189">"Zemljevid"</string>
+ <string name="map" msgid="5441053548030107189">"Odpri zemljevid"</string>
<string name="map_desc" msgid="1836995341943772348">"Poišči izbrani naslov na zemljevidu"</string>
<string name="browse" msgid="1245903488306147205">"Odpri"</string>
<string name="browse_desc" msgid="8220976549618935044">"Odpri izbrani URL"</string>
- <string name="sms" msgid="4560537514610063430">"Sporočilo"</string>
+ <string name="sms" msgid="4560537514610063430">"Pošlji SMS"</string>
<string name="sms_desc" msgid="7526588350969638809">"Pošlji sporočilo na izbrano telefonsko številko"</string>
<string name="add_contact" msgid="7867066569670597203">"Dodaj"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Dodaj med stike"</string>
- <string name="view_calendar" msgid="979609872939597838">"Ogled"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Ogled izbranega časa v koledarju"</string>
+ <string name="view_calendar" msgid="979609872939597838">"Prikaži"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Prikaži izbrani čas v koledarju"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Dodaj na razpored"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Razporedi dogodek na izbrano uro"</string>
- <string name="view_flight" msgid="7691640491425680214">"Sledenje"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Sledi"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Sledi izbranemu letu"</string>
<string name="translate" msgid="9218619809342576858">"Prevedi"</string>
- <string name="translate_desc" msgid="4502367770068777202">"Prevod izbranega besedila"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Prevedi izbrano besedilo"</string>
<string name="define" msgid="7394820043869954211">"Opredeli"</string>
<string name="define_desc" msgid="7910883642444919726">"Opredeli izbrano besedilo"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Prostor za shranjevanje bo pošel"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 25171b8..51ac513 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1096,10 +1096,10 @@
<string name="add_contact" msgid="7867066569670597203">"Ongeza"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Ongeza kwenye anwani"</string>
<string name="view_calendar" msgid="979609872939597838">"Angalia"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Angalia wakati uliyochagua katika kalenda"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Ratiba"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Angalia wakati uliochagua katika kalenda"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Ratibu"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Ratibu tukio la wakati uliochagua"</string>
- <string name="view_flight" msgid="7691640491425680214">"Toleo"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Fuatilia"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Fuatilia ndege uliyochagua"</string>
<string name="translate" msgid="9218619809342576858">"Tafsiri"</string>
<string name="translate_desc" msgid="4502367770068777202">"Tafsiri maandishi yaliyochaguliwa"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 492bad5..f59cfcb 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1091,7 +1091,7 @@
<string name="map_desc" msgid="1836995341943772348">"搵出指定地址"</string>
<string name="browse" msgid="1245903488306147205">"開啟"</string>
<string name="browse_desc" msgid="8220976549618935044">"打開指定網址"</string>
- <string name="sms" msgid="4560537514610063430">"短訊"</string>
+ <string name="sms" msgid="4560537514610063430">"發短訊"</string>
<string name="sms_desc" msgid="7526588350969638809">"Send 短訊去指定電話號碼"</string>
<string name="add_contact" msgid="7867066569670597203">"新增"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"加入通訊錄"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index f8004ea..ab4bd05 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -271,6 +271,9 @@
<!-- Additional flag from base permission type: this permission will be granted to the
wellbeing app, as defined by the OEM. -->
<flag name="wellbeing" value="0x20000" />
+ <!-- Additional flag from base permission type: this permission can be automatically
+ granted to the document manager -->
+ <flag name="documenter" value="0x40000" />
</attr>
<!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 101f92b..97a21a5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -222,6 +222,11 @@
so that applications can still use their own mechanisms. -->
<bool name="config_enableAutoPowerModes">false</bool>
+ <!-- Whether (if true) this is a kind of device that can be moved around (eg. phone/laptop),
+ or (if false) something for which movement is either not measurable or should not count
+ toward power states (eg. tv/soundbar). -->
+ <bool name="config_autoPowerModeUseMotionSensor">true</bool>
+
<!-- The threshold angle for any motion detection in auto-power save modes.
In hundreths of a degree. -->
<integer name="config_autoPowerModeThresholdAngle">200</integer>
@@ -3592,4 +3597,9 @@
<!-- Component name for default assistant on this device -->
<string name="config_defaultAssistantComponentName">#+UNSET</string>
+ <!-- Class name for the InputEvent compatibility processor override.
+ Empty string means use the default compatibility processor
+ (android.view.InputEventCompatProcessor). -->
+ <string name="config_inputEventCompatProcessorOverrideClassName" translatable="false"></string>
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a33f6b2..cab01f9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1215,6 +1215,15 @@
<string name="permdesc_manageOwnCalls">Allows the app to route its calls through the system in
order to improve the calling experience.</string>
+ <!-- Title of an application permission. When granted the app is allowed to be enabled as
+ a companion app. [CHAR LIMIT=NONE]-->
+ <string name="permlab_callCompanionApp">see and control calls through the system.</string>
+ <!-- Description of an application permission. When granted the app is allowed to be enabled as
+ a companion app. [CHAR LIMIT=NONE]-->
+ <string name="permdesc_callCompanionApp">Allows the app to see and control ongoing calls on the
+ device. This includes information such as call numbers for calls and the state of the
+ calls.</string>
+
<!-- Title of an application permission. When granted the user is giving access to a third
party app to continue a call which originated in another app. For example, the user
could be in a voice call over their carrier's mobile network, and a third party video
@@ -1397,6 +1406,16 @@
re-enables the keylock when the call is finished.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permlab_getAndRequestScreenLockComplexity">get and request screen lock complexity</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permdesc_getAndRequestScreenLockComplexity">Allows the app to learn the screen
+ lock complexity level (high, medium, low or none), which indicates the possible range of
+ length and type of the screen lock. The app can also suggest to users that they update the
+ screen lock to a certain level but users can freely ignore and navigate away. Note that the
+ screen lock is not stored in plaintext so the app does not know the exact password.
+ </string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
<string name="permlab_useBiometric">use biometric hardware</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
<string name="permdesc_useBiometric">Allows the app to use biometric hardware for authentication</string>
@@ -3323,6 +3342,15 @@
<!-- Notification action name for opening the wifi picker, showing the user all the nearby networks. -->
<string name="wifi_available_action_all_networks">All networks</string>
+ <!-- Notification title for a connection to a app suggested wireless network.-->
+ <string name="wifi_suggestion_title">Connected to Wi\u2011Fi network proposed by <xliff:g id="name" example="App123">%s</xliff:g></string>
+ <!-- Notification content for a connection to a app suggested wireless network.-->
+ <string name="wifi_suggestion_content">Do you want to let <xliff:g id="name" example="App123">%s</xliff:g> propose networks for you?</string>
+ <!-- Notification action for allowing app specified in the notification body.-->
+ <string name="wifi_suggestion_action_allow_app">Yes</string>
+ <!-- Notification action for disallowing app specified in the notification body.-->
+ <string name="wifi_suggestion_action_disallow_app">No</string>
+
<!--Notification title for Wi-Fi Wake onboarding. This is displayed the first time a user disables Wi-Fi with the feature enabled. -->
<string name="wifi_wakeup_onboarding_title">Wi\u2011Fi will turn on automatically</string>
<!--Notification subtext for Wi-Fi Wake onboarding.-->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b24cdba..161e416 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -265,6 +265,7 @@
<java-symbol type="integer" name="config_autoPowerModeAnyMotionSensor" />
<java-symbol type="bool" name="config_autoPowerModePreferWristTilt" />
<java-symbol type="bool" name="config_autoPowerModePrefetchLocation" />
+ <java-symbol type="bool" name="config_autoPowerModeUseMotionSensor" />
<java-symbol type="bool" name="config_enable_emergency_call_while_sim_locked" />
<java-symbol type="bool" name="config_enable_puk_unlock_screen" />
<java-symbol type="bool" name="config_disableLockscreenByDefault" />
@@ -301,6 +302,7 @@
<java-symbol type="bool" name="config_enableWallpaperService" />
<java-symbol type="bool" name="config_checkWallpaperAtBoot" />
<java-symbol type="string" name="config_wallpaperManagerServiceName" />
+ <java-symbol type="string" name="config_inputEventCompatProcessorOverrideClassName" />
<java-symbol type="bool" name="config_enableUpdateableTimeZoneRules" />
<java-symbol type="bool" name="config_timeZoneRulesUpdateTrackingEnabled" />
<java-symbol type="string" name="config_timeZoneRulesUpdaterPackage" />
@@ -1988,6 +1990,10 @@
<java-symbol type="string" name="wifi_available_content_failed_to_connect" />
<java-symbol type="string" name="wifi_available_action_connect" />
<java-symbol type="string" name="wifi_available_action_all_networks" />
+ <java-symbol type="string" name="wifi_suggestion_title" />
+ <java-symbol type="string" name="wifi_suggestion_content" />
+ <java-symbol type="string" name="wifi_suggestion_action_allow_app" />
+ <java-symbol type="string" name="wifi_suggestion_action_disallow_app" />
<java-symbol type="string" name="wifi_wakeup_onboarding_title" />
<java-symbol type="string" name="wifi_wakeup_onboarding_subtext" />
<java-symbol type="string" name="wifi_wakeup_onboarding_action_disable" />
diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
index d289f1f..9b5b725 100644
--- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
+++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
@@ -16,9 +16,16 @@
package android.app.admin;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import android.app.admin.PasswordMetrics.PasswordComplexityBucket;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -164,4 +171,126 @@
}
+
+ @Test
+ public void testConstructQuality() {
+ PasswordMetrics expected = new PasswordMetrics();
+ expected.quality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+
+ PasswordMetrics actual = new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testDetermineComplexity_none() {
+ assertEquals(PASSWORD_COMPLEXITY_NONE,
+ PasswordMetrics.computeForPassword("").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_lowSomething() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW,
+ new PasswordMetrics(PASSWORD_QUALITY_SOMETHING).determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_lowNumeric() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW,
+ PasswordMetrics.computeForPassword("1234").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_lowNumericComplex() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW,
+ PasswordMetrics.computeForPassword("124").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_lowAlphabetic() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW,
+ PasswordMetrics.computeForPassword("a!").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_lowAlphanumeric() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW,
+ PasswordMetrics.computeForPassword("a!1").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_mediumNumericComplex() {
+ assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
+ PasswordMetrics.computeForPassword("1238").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_mediumAlphabetic() {
+ assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
+ PasswordMetrics.computeForPassword("ab!c").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_mediumAlphanumeric() {
+ assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
+ PasswordMetrics.computeForPassword("ab!1").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_highNumericComplex() {
+ assertEquals(PASSWORD_COMPLEXITY_HIGH,
+ PasswordMetrics.computeForPassword("12389647!").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_highAlphabetic() {
+ assertEquals(PASSWORD_COMPLEXITY_HIGH,
+ PasswordMetrics.computeForPassword("alphabetic!").determineComplexity());
+ }
+
+ @Test
+ public void testDetermineComplexity_highAlphanumeric() {
+ assertEquals(PASSWORD_COMPLEXITY_HIGH,
+ PasswordMetrics.computeForPassword("alphanumeric123!").determineComplexity());
+ }
+
+ @Test
+ public void testComplexityLevelToBucket_none() {
+ PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
+ PASSWORD_COMPLEXITY_NONE).getMetrics();
+
+ for (PasswordMetrics metrics : bucket) {
+ assertEquals(PASSWORD_COMPLEXITY_NONE, metrics.determineComplexity());
+ }
+ }
+
+ @Test
+ public void testComplexityLevelToBucket_low() {
+ PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
+ PASSWORD_COMPLEXITY_LOW).getMetrics();
+
+ for (PasswordMetrics metrics : bucket) {
+ assertEquals(PASSWORD_COMPLEXITY_LOW, metrics.determineComplexity());
+ }
+ }
+
+ @Test
+ public void testComplexityLevelToBucket_medium() {
+ PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
+ PASSWORD_COMPLEXITY_MEDIUM).getMetrics();
+
+ for (PasswordMetrics metrics : bucket) {
+ assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metrics.determineComplexity());
+ }
+ }
+
+ @Test
+ public void testComplexityLevelToBucket_high() {
+ PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
+ PASSWORD_COMPLEXITY_HIGH).getMetrics();
+
+ for (PasswordMetrics metrics : bucket) {
+ assertEquals(PASSWORD_COMPLEXITY_HIGH, metrics.determineComplexity());
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
index 1f047f9e..28aaf1e 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
@@ -16,18 +16,22 @@
package android.app.usage;
-import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.END_OF_DAY;
+import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
-import static android.app.usage.UsageEvents.Event.MOVE_TO_BACKGROUND;
-import static android.app.usage.UsageEvents.Event.MOVE_TO_FOREGROUND;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import android.app.usage.UsageEvents.Event;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -120,10 +124,10 @@
left.mBeginTimeStamp = 100000;
left.mTotalTimeInForeground = 10;
- left.mLastForegroundActivityEventMap.put("com.test.activity1", MOVE_TO_FOREGROUND);
- left.mLastForegroundActivityEventMap.put("com.test.activity2", MOVE_TO_FOREGROUND);
- left.mLastForegroundServiceEventMap.put("com.test.service1", FOREGROUND_SERVICE_START);
- left.mLastForegroundServiceEventMap.put("com.test.service2", FOREGROUND_SERVICE_START);
+ left.mActivities.put(1, Event.ACTIVITY_RESUMED);
+ left.mActivities.put(2, Event.ACTIVITY_RESUMED);
+ left.mForegroundServices.put("com.test.service1", FOREGROUND_SERVICE_START);
+ left.mForegroundServices.put("com.test.service2", FOREGROUND_SERVICE_START);
Parcel p = Parcel.obtain();
left.writeToParcel(p, 0);
@@ -133,37 +137,38 @@
}
@Test
- public void testForegroundActivity() {
+ public void testActivity() {
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.activity1", 200000, MOVE_TO_FOREGROUND);
+ left.update("com.test.activity1", 200000, Event.ACTIVITY_RESUMED, 1);
assertEquals(left.mLastTimeUsed, 200000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
assertEquals(left.mLaunchCount, 1);
+ assertEquals(left.mTotalTimeInForeground, 0);
+ assertEquals(left.mTotalTimeVisible, 0);
- left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 350000, ACTIVITY_PAUSED, 1);
assertEquals(left.mLastTimeUsed, 350000);
- assertFalse(left.mLastForegroundActivityEventMap.containsKey("com.test.activity1"));
+ assertEquals(left.mLastTimeVisible, 350000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 350000 - 200000);
- }
+ assertEquals(left.mTotalTimeVisible, 350000 - 200000);
- @Test
- public void testEvent_CONTINUE_PREVIOUS_DAY() {
- left.mPackageName = "com.test";
- left.mBeginTimeStamp = 100000;
-
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
- assertEquals(left.mLastTimeUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLaunchCount, 0);
-
- left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 400000, ACTIVITY_STOPPED, 1);
assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
- assertEquals(left.mTotalTimeInForeground, 350000 - 100000);
+ assertEquals(left.mLastTimeVisible, 400000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
+ assertEquals(left.mTotalTimeInForeground, 350000 - 200000);
+ assertEquals(left.mTotalTimeVisible, 400000 - 200000);
+
+ left.update("com.test.activity1", 500000, ACTIVITY_DESTROYED, 1);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertEquals(left.mLastTimeVisible, 400000);
+ assertTrue(left.mActivities.indexOfKey(1) < 0);
+ assertEquals(left.mTotalTimeInForeground, 350000 - 200000);
+ assertEquals(left.mTotalTimeVisible, 400000 - 200000);
}
@Test
@@ -171,93 +176,143 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
+ left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1);
assertEquals(left.mLastTimeUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLaunchCount, 0);
-
- left.update(null, 350000, END_OF_DAY);
- assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(END_OF_DAY));
- assertEquals(left.mTotalTimeInForeground, 350000 - 100000);
- }
-
- @Test
- public void testForegroundActivityEventSequence() {
- left.mPackageName = "com.test";
- left.mBeginTimeStamp = 100000;
-
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
- assertEquals(left.mLastTimeUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLaunchCount, 0);
-
- left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
- assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
- assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/);
-
- left.update("com.test.activity1", 450000, MOVE_TO_FOREGROUND);
- assertEquals(left.mLastTimeUsed, 450000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(MOVE_TO_FOREGROUND));
- assertEquals(left.mTotalTimeInForeground, 250000);
-
- left.update("com.test.activity1", 500000, MOVE_TO_BACKGROUND);
- assertEquals(left.mLastTimeUsed, 500000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
- assertEquals(left.mTotalTimeInForeground, 250000 + 50000 /*500000 - 450000*/);
- }
-
- @Test
- public void testForegroundActivityEventOutOfSequence() {
- left.mPackageName = "com.test";
- left.mBeginTimeStamp = 100000;
-
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
- assertEquals(left.mLastTimeUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLaunchCount, 0);
-
- left.update("com.test.activity1", 150000, MOVE_TO_FOREGROUND);
- assertEquals(left.mLastTimeUsed, 150000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
assertEquals(left.mLaunchCount, 1);
- assertEquals(left.mTotalTimeInForeground, 50000 /*150000 - 100000*/);
- left.update("com.test.activity1", 200000, MOVE_TO_FOREGROUND);
+ left.update(null, 350000, END_OF_DAY, 0);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertEquals(left.mLastTimeVisible, 350000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mTotalTimeInForeground, 350000 - 100000);
+ assertEquals(left.mTotalTimeVisible, 350000 - 100000);
+ }
+
+ @Test
+ public void testEvent_ACTIVITY_PAUSED() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, ACTIVITY_PAUSED, 1);
+ assertEquals(left.mLastTimeUsed, 0);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
+
+ left.update("com.test.activity1", 200000, Event.ACTIVITY_RESUMED, 1);
assertEquals(left.mLastTimeUsed, 200000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mTotalTimeInForeground, 0);
+ assertEquals(left.mTotalTimeVisible, 200000 - 100000);
+
+ left.update("com.test.activity1", 300000, ACTIVITY_PAUSED, 1);
+ assertEquals(left.mLastTimeUsed, 300000);
+ assertEquals(left.mLastTimeVisible, 300000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
+ assertEquals(left.mTotalTimeInForeground, 300000 - 200000);
+ assertEquals(left.mTotalTimeVisible, 300000 - 100000);
+
+ left.update("com.test.activity1", 400000, ACTIVITY_STOPPED, 1);
+ assertEquals(left.mLastTimeUsed, 300000);
+ assertEquals(left.mLastTimeVisible, 400000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
+ assertEquals(left.mTotalTimeInForeground, 300000 - 200000);
+ assertEquals(left.mTotalTimeVisible, 400000 - 100000);
+ }
+
+ @Test
+ public void testEvent_CHANGE_TO_INVISIBLE() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_RESUMED);
+
+ left.update("com.test.activity1", 200000, ACTIVITY_STOPPED, 1);
+ assertEquals(left.mLastTimeUsed, 200000);
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
+ assertEquals(left.mTotalTimeInForeground, 200000 - 100000);
+ assertEquals(left.mTotalTimeVisible, 200000 - 100000);
+
+ left.update("com.test.activity1", 300000, ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 300000);
+ assertEquals(left.mLastTimeVisible, 300000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_RESUMED);
+ assertEquals(left.mTotalTimeInForeground, 200000 - 100000);
+ assertEquals(left.mTotalTimeVisible, 200000 - 100000);
+ }
+
+ @Test
+ public void testEvent_ACTIVITY_DESTROYED() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_RESUMED);
+
+ left.update("com.test.activity1", 200000, ACTIVITY_DESTROYED, 1);
+ assertEquals(left.mLastTimeUsed, 200000);
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertTrue(left.mActivities.indexOfKey(1) < 0);
+ assertEquals(left.mTotalTimeInForeground, 200000 - 100000);
+ assertEquals(left.mTotalTimeVisible, 200000 - 100000);
+ }
+
+ @Test
+ public void testActivityEventOutOfOrder() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mLaunchCount, 1);
+ assertEquals(left.mTotalTimeInForeground, 0);
+ assertEquals(left.mTotalTimeVisible, 0);
+
+ left.update("com.test.activity1", 200000, Event.ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 200000);
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
assertEquals(left.mLaunchCount, 2);
assertEquals(left.mTotalTimeInForeground, 100000);
+ assertEquals(left.mTotalTimeVisible, 100000 /*200000 - 100000*/);
- left.update("com.test.activity1", 250000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 250000, ACTIVITY_PAUSED, 1);
assertEquals(left.mLastTimeUsed, 250000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mLastTimeVisible, 250000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 150000);
+ assertEquals(left.mTotalTimeVisible, 150000 /*250000 - 100000*/);
- left.update("com.test.activity1", 300000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 300000, ACTIVITY_PAUSED, 1);
assertEquals(left.mLastTimeUsed, 250000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mLastTimeVisible, 300000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 150000);
+ assertEquals(left.mTotalTimeVisible, 200000 /*300000 - 100000*/);
- left.update("com.test.activity1", 350000, MOVE_TO_FOREGROUND);
+ left.update("com.test.activity1", 350000, Event.ACTIVITY_RESUMED, 1);
assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLastTimeVisible, 350000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
assertEquals(left.mTotalTimeInForeground, 150000);
+ assertEquals(left.mTotalTimeVisible, 250000 /*350000 - 100000*/);
- left.update("com.test.activity1", 400000, END_OF_DAY);
+ left.update("com.test.activity1", 400000, END_OF_DAY, 1);
assertEquals(left.mLastTimeUsed, 400000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(END_OF_DAY));
+ assertEquals(left.mLastTimeVisible, 400000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
assertEquals(left.mTotalTimeInForeground, 200000);
+ assertEquals(left.mTotalTimeVisible, 300000 /*400000 - 100000*/);
}
@Test
@@ -265,28 +320,41 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
- left.update("com.test.activity2", 100000, CONTINUE_PREVIOUS_DAY);
+ left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1);
+ left.update("com.test.activity2", 100000, Event.ACTIVITY_RESUMED, 2);
assertEquals(left.mLastTimeUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLaunchCount, 0);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mActivities.get(2), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mLaunchCount, 2);
- left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 350000, ACTIVITY_PAUSED, 1);
assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mLastTimeVisible, 350000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/);
+ assertEquals(left.mTotalTimeVisible, 250000 /*350000 - 100000*/);
- left.update("com.test.activity2", 450000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity2", 450000, ACTIVITY_PAUSED, 2);
assertEquals(left.mLastTimeUsed, 450000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), null);
+ assertEquals(left.mLastTimeVisible, 450000);
+ assertEquals(left.mActivities.get(2), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 250000 + 100000 /*450000 - 350000*/);
+ assertEquals(left.mTotalTimeVisible, 250000 + 100000 /*450000 - 350000*/);
- left.update(null, 500000, END_OF_DAY);
+ left.update("com.test.activity1", 550000, ACTIVITY_STOPPED, 1);
assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mLastTimeVisible, 550000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
assertEquals(left.mTotalTimeInForeground, 350000);
+ assertEquals(left.mTotalTimeVisible, 350000 + 100000 /*550000 - 450000*/);
+
+ left.update("com.test.activity2", 650000, ACTIVITY_STOPPED, 2);
+ assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mLastTimeVisible, 650000);
+ assertEquals(left.mActivities.get(2), ACTIVITY_STOPPED);
+ assertEquals(left.mTotalTimeInForeground, 350000);
+ assertEquals(left.mTotalTimeVisible, 450000 + 100000 /*650000 - 550000*/);
}
@Test
@@ -294,15 +362,14 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.service1", 200000, FOREGROUND_SERVICE_START);
+ left.update("com.test.service1", 200000, FOREGROUND_SERVICE_START, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 200000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(FOREGROUND_SERVICE_START));
- assertEquals(left.mLaunchCount, 0);
- left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 200000);
}
@@ -311,15 +378,15 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.service1", 100000, CONTINUING_FOREGROUND_SERVICE);
+ left.update("com.test.service1", 100000,
+ CONTINUING_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLaunchCount, 0);
- left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 100000);
}
@@ -329,16 +396,15 @@
left.mBeginTimeStamp = 100000;
left.update("com.test.service1", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLaunchCount, 0);
- left.update(null, 350000, ROLLOVER_FOREGROUND_SERVICE);
+ left.update(null, 350000, ROLLOVER_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
- new Integer(ROLLOVER_FOREGROUND_SERVICE));
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
+ new Integer(CONTINUING_FOREGROUND_SERVICE));
assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 100000);
}
@@ -348,27 +414,28 @@
left.mBeginTimeStamp = 100000;
left.update("com.test.service1", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
assertEquals(left.mLaunchCount, 0);
- left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 /*350000 - 100000*/);
- left.update("com.test.service1", 450000, FOREGROUND_SERVICE_START);
+ left.update("com.test.service1", 450000, FOREGROUND_SERVICE_START, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 450000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(FOREGROUND_SERVICE_START));
assertEquals(left.mTotalTimeForegroundServiceUsed, 250000);
- left.update("com.test.service1", 500000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 500000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 500000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
- assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 + 50000 /*500000 - 450000*/);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed,
+ 250000 + 50000 /*500000 - 450000*/);
}
@Test
@@ -377,27 +444,27 @@
left.mBeginTimeStamp = 100000;
left.update("com.test.service1", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
left.update("com.test.service2", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"),
+ assertEquals(left.mForegroundServices.get("com.test.service2"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLaunchCount, 0);
- left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 /*350000 - 100000*/);
- left.update("com.test.service2", 450000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service2", 450000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 450000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), null);
- assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 + 100000 /*450000 - 350000*/);
+ assertEquals(left.mForegroundServices.get("com.test.service2"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed,
+ 250000 + 100000 /*450000 - 350000*/);
- left.update(null, 500000, ROLLOVER_FOREGROUND_SERVICE);
+ left.update(null, 500000, ROLLOVER_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 450000);
assertEquals(left.mTotalTimeForegroundServiceUsed, 350000);
}
@@ -407,76 +474,117 @@
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
- left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
- left.update("com.test.activity2", 100000, CONTINUE_PREVIOUS_DAY);
+ left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1);
+ left.update("com.test.activity2", 100000, Event.ACTIVITY_RESUMED, 2);
left.update("com.test.service1", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
left.update("com.test.service2", 100000,
- CONTINUING_FOREGROUND_SERVICE);
+ CONTINUING_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeUsed, 100000);
assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"),
- new Integer(CONTINUE_PREVIOUS_DAY));
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mActivities.get(2), Event.ACTIVITY_RESUMED);
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"),
+ assertEquals(left.mForegroundServices.get("com.test.service2"),
new Integer(CONTINUING_FOREGROUND_SERVICE));
- assertEquals(left.mLaunchCount, 0);
+ assertEquals(left.mLaunchCount, 2);
- left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity1", 350000, ACTIVITY_PAUSED, 1);
assertEquals(left.mLastTimeUsed, 350000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mLastTimeVisible, 350000);
+ assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/);
+ assertEquals(left.mTotalTimeVisible, 250000 /*350000 - 100000*/);
- left.update("com.test.service1", 400000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service1", 400000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 400000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mForegroundServices.get("com.test.service1"), null);
assertEquals(left.mTotalTimeForegroundServiceUsed, 300000 /*400000 - 100000*/);
- left.update("com.test.activity2", 450000, MOVE_TO_BACKGROUND);
+ left.update("com.test.activity2", 450000, ACTIVITY_PAUSED, 2);
assertEquals(left.mLastTimeUsed, 450000);
- assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), null);
+ assertEquals(left.mLastTimeVisible, 450000);
+ assertEquals(left.mActivities.get(2), ACTIVITY_PAUSED);
assertEquals(left.mTotalTimeInForeground, 250000 + 100000 /*450000 - 350000*/);
+ assertEquals(left.mTotalTimeVisible, 250000 + 100000 /*450000 - 350000*/);
- left.update("com.test.service2", 500000, FOREGROUND_SERVICE_STOP);
+ left.update("com.test.service2", 500000, FOREGROUND_SERVICE_STOP, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 500000);
- assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), null);
- assertEquals(left.mTotalTimeForegroundServiceUsed, 300000 + 100000 /*500000 - 400000*/);
+ assertEquals(left.mForegroundServices.get("com.test.service2"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed,
+ 300000 + 100000 /*500000 - 400000*/);
- left.update(null, 550000, END_OF_DAY);
+ left.update(null, 550000, END_OF_DAY, 0);
assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mLastTimeVisible, 550000);
assertEquals(left.mTotalTimeInForeground, 350000);
- left.update(null, 550000, ROLLOVER_FOREGROUND_SERVICE);
+ assertEquals(left.mTotalTimeVisible, 450000);
+
+ left.update(null, 550000, ROLLOVER_FOREGROUND_SERVICE, 0);
assertEquals(left.mLastTimeForegroundServiceUsed, 500000);
assertEquals(left.mTotalTimeForegroundServiceUsed, 400000);
}
+ @Test
+ public void testEvent_FLUSH_TO_DISK() {
+ testClosingEvent(FLUSH_TO_DISK);
+ }
+
+ private void testClosingEvent(int eventType) {
+ // When these three closing events are received, all open activities/services need to be
+ // closed and usage stats are updated.
+ if (eventType != FLUSH_TO_DISK) {
+ fail("Closing eventType must be one of FLUSH_TO_DISK");
+ }
+
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastTimeVisible, 100000);
+ assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED);
+
+ left.update("com.test.service1", 150000, FOREGROUND_SERVICE_START, 0);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 150000);
+ assertEquals(left.mForegroundServices.get("com.test.service1"),
+ new Integer(FOREGROUND_SERVICE_START));
+
+ left.update(null, 200000, eventType, 0);
+ assertEquals(left.mLastTimeUsed, 200000);
+ assertEquals(left.mLastTimeVisible, 200000);
+ assertEquals(left.mTotalTimeInForeground, 200000 - 100000);
+ assertEquals(left.mTotalTimeVisible, 200000 - 100000);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 200000);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 200000 - 150000);
+ }
+
void compareUsageStats(UsageStats us1, UsageStats us2) {
assertEquals(us1.mPackageName, us2.mPackageName);
assertEquals(us1.mBeginTimeStamp, us2.mBeginTimeStamp);
assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
+ assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible);
assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed);
assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount);
- assertEquals(us1.mLastForegroundActivityEventMap.size(),
- us2.mLastForegroundActivityEventMap.size());
- for (int i = 0; i < us1.mLastForegroundActivityEventMap.size(); i++) {
- assertEquals(us1.mLastForegroundActivityEventMap.keyAt(i),
- us2.mLastForegroundActivityEventMap.keyAt(i));
- assertEquals(us1.mLastForegroundActivityEventMap.valueAt(i),
- us2.mLastForegroundActivityEventMap.valueAt(i));
+ assertEquals(us1.mActivities.size(),
+ us2.mActivities.size());
+ for (int i = 0; i < us1.mActivities.size(); i++) {
+ assertEquals(us1.mActivities.keyAt(i),
+ us2.mActivities.keyAt(i));
+ assertEquals(us1.mActivities.valueAt(i),
+ us2.mActivities.valueAt(i));
}
- assertEquals(us1.mLastForegroundServiceEventMap.size(),
- us2.mLastForegroundServiceEventMap.size());
- for (int i = 0; i < us1.mLastForegroundServiceEventMap.size(); i++) {
- assertEquals(us1.mLastForegroundServiceEventMap.keyAt(i),
- us2.mLastForegroundServiceEventMap.keyAt(i));
- assertEquals(us1.mLastForegroundServiceEventMap.valueAt(i),
- us2.mLastForegroundServiceEventMap.valueAt(i));
+ assertEquals(us1.mForegroundServices.size(),
+ us2.mForegroundServices.size());
+ for (int i = 0; i < us1.mForegroundServices.size(); i++) {
+ assertEquals(us1.mForegroundServices.keyAt(i),
+ us2.mForegroundServices.keyAt(i));
+ assertEquals(us1.mForegroundServices.valueAt(i),
+ us2.mForegroundServices.valueAt(i));
}
assertEquals(us1.mChooserCounts, us2.mChooserCounts);
}
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index 3d15eb9..a0dca2c 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -133,7 +133,8 @@
public void buildForStaticLayout() {
MeasuredParagraph mt = null;
- mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, false, false, null);
+ mt = MeasuredParagraph.buildForStaticLayout(
+ PAINT, "XXX", 0, 3, LTR, false, false, null /* no hint */, null);
assertNotNull(mt);
assertNotNull(mt.getChars());
assertEquals("XXX", charsToString(mt.getChars()));
@@ -147,8 +148,8 @@
assertNotNull(mt.getMeasuredText());
// Recycle it
- MeasuredParagraph mt2 =
- MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, false, false, mt);
+ MeasuredParagraph mt2 = MeasuredParagraph.buildForStaticLayout(
+ PAINT, "_VVV_", 1, 4, RTL, false, false, null /* no hint */, mt);
assertEquals(mt2, mt);
assertNotNull(mt2.getChars());
assertEquals("VVV", charsToString(mt.getChars()));
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index ed80cd7..2ad6028 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -19,6 +19,7 @@
import static android.view.InsetsState.TYPE_TOP_BAR;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
+import static org.mockito.Mockito.mock;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
@@ -33,7 +34,7 @@
@RunWith(AndroidJUnit4.class)
public class InsetsControllerTest {
- private InsetsController mController = new InsetsController();
+ private InsetsController mController = new InsetsController(mock(ViewRootImpl.class));
private SurfaceSession mSession = new SurfaceSession();
private SurfaceControl mLeash;
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 5a20ba2..6d0f984 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -53,7 +53,7 @@
.setName("testSurface")
.build();
mConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, new InsetsState(),
- () -> mMockTransaction);
+ () -> mMockTransaction, mMockController);
mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash));
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
index f0faaf6..4a6c093 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -16,6 +16,9 @@
package android.view.textclassifier;
+import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_LOCAL;
+import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_REMOTE;
+
import static com.google.common.truth.Truth.assertThat;
import android.app.Person;
@@ -27,16 +30,26 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Locale;
+import java.util.function.Function;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ActionsSuggestionsHelperTest {
+ private static final String LOCALE_TAG = Locale.US.toLanguageTag();
+ private static final Function<CharSequence, String> LANGUAGE_DETECTOR =
+ charSequence -> LOCALE_TAG;
+
@Test
public void testToNativeMessages_emptyInput() {
ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(Collections.emptyList());
+ ActionsSuggestionsHelper.toNativeMessages(
+ Collections.emptyList(), LANGUAGE_DETECTOR);
assertThat(conversationMessages).isEmpty();
}
@@ -44,114 +57,89 @@
@Test
public void testToNativeMessages_noTextMessages() {
ConversationActions.Message messageWithoutText =
- new ConversationActions.Message.Builder().build();
+ new ConversationActions.Message.Builder(PERSON_USER_REMOTE).build();
ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
ActionsSuggestionsHelper.toNativeMessages(
- Collections.singletonList(messageWithoutText));
+ Collections.singletonList(messageWithoutText), LANGUAGE_DETECTOR);
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()
+ new ConversationActions.Message.Builder(userB)
.setText("first")
- .setAuthor(userB)
.build();
ConversationActions.Message secondMessage =
- new ConversationActions.Message.Builder()
+ new ConversationActions.Message.Builder(userA)
.setText("second")
- .setAuthor(userA)
.build();
ConversationActions.Message thirdMessage =
- new ConversationActions.Message.Builder()
+ new ConversationActions.Message.Builder(PERSON_USER_LOCAL)
.setText("third")
- .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
.build();
ConversationActions.Message fourthMessage =
- new ConversationActions.Message.Builder()
+ new ConversationActions.Message.Builder(userA)
.setText("fourth")
- .setAuthor(userA)
.build();
ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
ActionsSuggestionsHelper.toNativeMessages(
- Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+ Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage),
+ LANGUAGE_DETECTOR);
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);
+ assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2, 0);
+ assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
+ assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0, 0);
+ assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1, 0);
+ }
+
+ @Test
+ public void testToNativeMessages_referenceTime() {
+ ConversationActions.Message firstMessage =
+ new ConversationActions.Message.Builder(PERSON_USER_REMOTE)
+ .setText("first")
+ .setReferenceTime(createZonedDateTimeFromMsUtc(1000))
+ .build();
+ ConversationActions.Message secondMessage =
+ new ConversationActions.Message.Builder(PERSON_USER_REMOTE)
+ .setText("second")
+ .build();
+ ConversationActions.Message thirdMessage =
+ new ConversationActions.Message.Builder(PERSON_USER_REMOTE)
+ .setText("third")
+ .setReferenceTime(createZonedDateTimeFromMsUtc(2000))
+ .build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Arrays.asList(firstMessage, secondMessage, thirdMessage),
+ LANGUAGE_DETECTOR);
+
+ assertThat(conversationMessages).hasLength(3);
+ assertNativeMessage(conversationMessages[0], firstMessage.getText(), 1, 1000);
+ assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
+ assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 1, 2000);
+ }
+
+ private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneId.of("UTC"));
}
private static void assertNativeMessage(
ActionsSuggestionsModel.ConversationMessage nativeMessage,
CharSequence text,
- int userId) {
+ int userId,
+ long referenceTimeInMsUtc) {
assertThat(nativeMessage.getText()).isEqualTo(text.toString());
assertThat(nativeMessage.getUserId()).isEqualTo(userId);
+ assertThat(nativeMessage.getLocales()).isEqualTo(LOCALE_TAG);
+ assertThat(nativeMessage.getReferenceTimeMsUtc()).isEqualTo(referenceTimeInMsUtc);
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
index bae2be3..aaadefb 100644
--- a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
@@ -56,5 +56,7 @@
Intent intent = labeledIntent.getIntent();
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
+ assertThat(
+ intent.getBooleanExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, false)).isTrue();
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index aec4571..9b5c034 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -373,7 +373,10 @@
public void testSuggestConversationActions_textReplyOnly_maxThree() {
if (isTextClassifierDisabled()) return;
ConversationActions.Message message =
- new ConversationActions.Message.Builder().setText("Where are you?").build();
+ new ConversationActions.Message.Builder(
+ ConversationActions.Message.PERSON_USER_REMOTE)
+ .setText("Hello")
+ .build();
ConversationActions.TypeConfig typeConfig =
new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
.setIncludedTypes(
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f237344..0a2f057 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -141,6 +141,7 @@
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
+ <permission name="android.permission.CLEAR_APP_USER_DATA"/>
</privapp-permissions>
<privapp-permissions package="com.android.permissioncontroller">
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index 4ee45bf..70c90eb 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -65,7 +65,7 @@
*/
public DrawableWrapper(@Nullable Drawable dr) {
mState = null;
- mDrawable = dr;
+ setDrawable(dr);
}
/**
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 6f78651..2536619 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -19,6 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.Px;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -49,12 +50,17 @@
*/
public class MeasuredText {
private long mNativePtr;
+ private boolean mComputeHyphenation;
+ private boolean mComputeLayout;
private @NonNull char[] mChars;
// Use builder instead.
- private MeasuredText(long ptr, @NonNull char[] chars) {
+ private MeasuredText(long ptr, @NonNull char[] chars, boolean computeHyphenation,
+ boolean computeLayout) {
mNativePtr = ptr;
mChars = chars;
+ mComputeHyphenation = computeHyphenation;
+ mComputeLayout = computeLayout;
}
/**
@@ -172,6 +178,7 @@
private boolean mComputeHyphenation = false;
private boolean mComputeLayout = true;
private int mCurrentOffset = 0;
+ private @Nullable MeasuredText mHintMt = null;
/**
* Construct a builder.
@@ -188,6 +195,27 @@
}
/**
+ * Construct a builder with existing MeasuredText.
+ *
+ * The MeasuredText returned by build method will hold a reference of the text. Developer is
+ * not supposed to modify the text.
+ *
+ * @param text a text
+ */
+ public Builder(@NonNull MeasuredText text) {
+ Preconditions.checkNotNull(text);
+ mText = text.mChars;
+ mNativePtr = nInitBuilder();
+ if (!text.mComputeLayout) {
+ throw new IllegalArgumentException(
+ "The input MeasuredText must not be created with setComputeLayout(false).");
+ }
+ mComputeHyphenation = text.mComputeHyphenation;
+ mComputeLayout = text.mComputeLayout;
+ mHintMt = text;
+ }
+
+ /**
* Apply styles to the given length.
*
* Keeps an internal offset which increases at every append. The initial value for this
@@ -282,10 +310,16 @@
if (mCurrentOffset != mText.length) {
throw new IllegalStateException("Style info has not been provided for all text.");
}
+ if (mHintMt != null && mHintMt.mComputeHyphenation != mComputeHyphenation) {
+ throw new IllegalArgumentException(
+ "The hyphenation configuration is different from given hint MeasuredText");
+ }
try {
- long ptr = nBuildMeasuredText(mNativePtr, mText, mComputeHyphenation,
+ long hintPtr = (mHintMt == null) ? 0 : mHintMt.getNativePtr();
+ long ptr = nBuildMeasuredText(mNativePtr, hintPtr, mText, mComputeHyphenation,
mComputeLayout);
- MeasuredText res = new MeasuredText(ptr, mText);
+ final MeasuredText res = new MeasuredText(ptr, mText, mComputeHyphenation,
+ mComputeLayout);
sRegistry.registerNativeAllocation(res, ptr);
return res;
} finally {
@@ -339,6 +373,7 @@
private static native long nBuildMeasuredText(
/* Non Zero */ long nativeBuilderPtr,
+ long hintMtPtr,
@NonNull char[] text,
boolean computeHyphenation,
boolean computeLayout);
diff --git a/libs/androidfw/LocaleData.cpp b/libs/androidfw/LocaleData.cpp
index 889d166..020cef6 100644
--- a/libs/androidfw/LocaleData.cpp
+++ b/libs/androidfw/LocaleData.cpp
@@ -34,11 +34,11 @@
}
inline uint32_t dropRegion(uint32_t packed_locale) {
- return packed_locale & 0xFFFF0000lu;
+ return packed_locale & 0xFFFF0000LU;
}
inline bool hasRegion(uint32_t packed_locale) {
- return (packed_locale & 0x0000FFFFlu) != 0;
+ return (packed_locale & 0x0000FFFFLU) != 0;
}
const size_t SCRIPT_LENGTH = 4;
@@ -122,9 +122,9 @@
return (REPRESENTATIVE_LOCALES.count(packed_locale) != 0);
}
-const uint32_t US_SPANISH = 0x65735553lu; // es-US
-const uint32_t MEXICAN_SPANISH = 0x65734D58lu; // es-MX
-const uint32_t LATIN_AMERICAN_SPANISH = 0x6573A424lu; // es-419
+const uint32_t US_SPANISH = 0x65735553LU; // es-US
+const uint32_t MEXICAN_SPANISH = 0x65734D58LU; // es-MX
+const uint32_t LATIN_AMERICAN_SPANISH = 0x6573A424LU; // es-419
// The two locales es-US and es-MX are treated as special fallbacks for es-419.
// If there is no es-419, they are considered its equivalent.
@@ -225,8 +225,8 @@
}
const uint32_t ENGLISH_STOP_LIST[2] = {
- 0x656E0000lu, // en
- 0x656E8400lu, // en-001
+ 0x656E0000LU, // en
+ 0x656E8400LU, // en-001
};
const char ENGLISH_CHARS[2] = {'e', 'n'};
const char LATIN_CHARS[4] = {'L', 'a', 't', 'n'};
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 7c381ef..c276a23 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -1446,733 +1446,733 @@
});
std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
- 0x616145544C61746Ellu, // aa_Latn_ET
- 0x616247454379726Cllu, // ab_Cyrl_GE
- 0xC42047484C61746Ellu, // abr_Latn_GH
- 0x904049444C61746Ellu, // ace_Latn_ID
- 0x9C4055474C61746Ellu, // ach_Latn_UG
- 0x806047484C61746Ellu, // ada_Latn_GH
- 0xE06052554379726Cllu, // ady_Cyrl_RU
- 0x6165495241767374llu, // ae_Avst_IR
- 0x8480544E41726162llu, // aeb_Arab_TN
- 0x61665A414C61746Ellu, // af_Latn_ZA
- 0xC0C0434D4C61746Ellu, // agq_Latn_CM
- 0xB8E0494E41686F6Dllu, // aho_Ahom_IN
- 0x616B47484C61746Ellu, // ak_Latn_GH
- 0xA940495158737578llu, // akk_Xsux_IQ
- 0xB560584B4C61746Ellu, // aln_Latn_XK
- 0xCD6052554379726Cllu, // alt_Cyrl_RU
- 0x616D455445746869llu, // am_Ethi_ET
- 0xB9804E474C61746Ellu, // amo_Latn_NG
- 0xE5C049444C61746Ellu, // aoz_Latn_ID
- 0x8DE0544741726162llu, // apd_Arab_TG
- 0x6172454741726162llu, // ar_Arab_EG
- 0x8A20495241726D69llu, // arc_Armi_IR
- 0x8A204A4F4E626174llu, // arc_Nbat_JO
- 0x8A20535950616C6Dllu, // arc_Palm_SY
- 0xB620434C4C61746Ellu, // arn_Latn_CL
- 0xBA20424F4C61746Ellu, // aro_Latn_BO
- 0xC220445A41726162llu, // arq_Arab_DZ
- 0xE2204D4141726162llu, // ary_Arab_MA
- 0xE620454741726162llu, // arz_Arab_EG
- 0x6173494E42656E67llu, // as_Beng_IN
- 0x8240545A4C61746Ellu, // asa_Latn_TZ
- 0x9240555353676E77llu, // ase_Sgnw_US
- 0xCE4045534C61746Ellu, // ast_Latn_ES
- 0xA66043414C61746Ellu, // atj_Latn_CA
- 0x617652554379726Cllu, // av_Cyrl_RU
- 0x82C0494E44657661llu, // awa_Deva_IN
- 0x6179424F4C61746Ellu, // ay_Latn_BO
- 0x617A495241726162llu, // az_Arab_IR
- 0x617A415A4C61746Ellu, // az_Latn_AZ
- 0x626152554379726Cllu, // ba_Cyrl_RU
- 0xAC01504B41726162llu, // bal_Arab_PK
- 0xB40149444C61746Ellu, // ban_Latn_ID
- 0xBC014E5044657661llu, // bap_Deva_NP
- 0xC40141544C61746Ellu, // bar_Latn_AT
- 0xC801434D4C61746Ellu, // bas_Latn_CM
- 0xDC01434D42616D75llu, // bax_Bamu_CM
- 0x882149444C61746Ellu, // bbc_Latn_ID
- 0xA421434D4C61746Ellu, // bbj_Latn_CM
- 0xA04143494C61746Ellu, // bci_Latn_CI
- 0x626542594379726Cllu, // be_Cyrl_BY
- 0xA481534441726162llu, // bej_Arab_SD
- 0xB0815A4D4C61746Ellu, // bem_Latn_ZM
- 0xD88149444C61746Ellu, // bew_Latn_ID
- 0xE481545A4C61746Ellu, // bez_Latn_TZ
- 0x8CA1434D4C61746Ellu, // bfd_Latn_CM
- 0xC0A1494E54616D6Cllu, // bfq_Taml_IN
- 0xCCA1504B41726162llu, // bft_Arab_PK
- 0xE0A1494E44657661llu, // bfy_Deva_IN
- 0x626742474379726Cllu, // bg_Cyrl_BG
- 0x88C1494E44657661llu, // bgc_Deva_IN
- 0xB4C1504B41726162llu, // bgn_Arab_PK
- 0xDCC154524772656Bllu, // bgx_Grek_TR
- 0x84E1494E44657661llu, // bhb_Deva_IN
- 0xA0E1494E44657661llu, // bhi_Deva_IN
- 0xA8E150484C61746Ellu, // bhk_Latn_PH
- 0xB8E1494E44657661llu, // bho_Deva_IN
- 0x626956554C61746Ellu, // bi_Latn_VU
- 0xA90150484C61746Ellu, // bik_Latn_PH
- 0xB5014E474C61746Ellu, // bin_Latn_NG
- 0xA521494E44657661llu, // bjj_Deva_IN
- 0xB52149444C61746Ellu, // bjn_Latn_ID
- 0xB141434D4C61746Ellu, // bkm_Latn_CM
- 0xD14150484C61746Ellu, // bku_Latn_PH
- 0xCD61564E54617674llu, // blt_Tavt_VN
- 0x626D4D4C4C61746Ellu, // bm_Latn_ML
- 0xC1814D4C4C61746Ellu, // bmq_Latn_ML
- 0x626E424442656E67llu, // bn_Beng_BD
- 0x626F434E54696274llu, // bo_Tibt_CN
- 0xE1E1494E42656E67llu, // bpy_Beng_IN
- 0xA201495241726162llu, // bqi_Arab_IR
- 0xD60143494C61746Ellu, // bqv_Latn_CI
- 0x627246524C61746Ellu, // br_Latn_FR
- 0x8221494E44657661llu, // bra_Deva_IN
- 0x9E21504B41726162llu, // brh_Arab_PK
- 0xDE21494E44657661llu, // brx_Deva_IN
- 0x627342414C61746Ellu, // bs_Latn_BA
- 0xC2414C5242617373llu, // bsq_Bass_LR
- 0xCA41434D4C61746Ellu, // bss_Latn_CM
- 0xBA6150484C61746Ellu, // bto_Latn_PH
- 0xD661504B44657661llu, // btv_Deva_PK
- 0x828152554379726Cllu, // bua_Cyrl_RU
- 0x8A8159544C61746Ellu, // buc_Latn_YT
- 0x9A8149444C61746Ellu, // bug_Latn_ID
- 0xB281434D4C61746Ellu, // bum_Latn_CM
- 0x86A147514C61746Ellu, // bvb_Latn_GQ
- 0xB701455245746869llu, // byn_Ethi_ER
- 0xD701434D4C61746Ellu, // byv_Latn_CM
- 0x93214D4C4C61746Ellu, // bze_Latn_ML
- 0x636145534C61746Ellu, // ca_Latn_ES
- 0x9C424E474C61746Ellu, // cch_Latn_NG
- 0xBC42494E42656E67llu, // ccp_Beng_IN
- 0xBC42424443616B6Dllu, // ccp_Cakm_BD
- 0x636552554379726Cllu, // ce_Cyrl_RU
- 0x848250484C61746Ellu, // ceb_Latn_PH
- 0x98C255474C61746Ellu, // cgg_Latn_UG
- 0x636847554C61746Ellu, // ch_Latn_GU
- 0xA8E2464D4C61746Ellu, // chk_Latn_FM
- 0xB0E252554379726Cllu, // chm_Cyrl_RU
- 0xB8E255534C61746Ellu, // cho_Latn_US
- 0xBCE243414C61746Ellu, // chp_Latn_CA
- 0xC4E2555343686572llu, // chr_Cher_US
- 0x81224B4841726162llu, // cja_Arab_KH
- 0xB122564E4368616Dllu, // cjm_Cham_VN
- 0x8542495141726162llu, // ckb_Arab_IQ
- 0x636F46524C61746Ellu, // co_Latn_FR
- 0xBDC24547436F7074llu, // cop_Copt_EG
- 0xC9E250484C61746Ellu, // cps_Latn_PH
- 0x6372434143616E73llu, // cr_Cans_CA
- 0xA622434143616E73llu, // crj_Cans_CA
- 0xAA22434143616E73llu, // crk_Cans_CA
- 0xAE22434143616E73llu, // crl_Cans_CA
- 0xB222434143616E73llu, // crm_Cans_CA
- 0xCA2253434C61746Ellu, // crs_Latn_SC
- 0x6373435A4C61746Ellu, // cs_Latn_CZ
- 0x8642504C4C61746Ellu, // csb_Latn_PL
- 0xDA42434143616E73llu, // csw_Cans_CA
- 0x8E624D4D50617563llu, // ctd_Pauc_MM
- 0x637552554379726Cllu, // cu_Cyrl_RU
- 0x63754247476C6167llu, // cu_Glag_BG
- 0x637652554379726Cllu, // cv_Cyrl_RU
- 0x637947424C61746Ellu, // cy_Latn_GB
- 0x6461444B4C61746Ellu, // da_Latn_DK
- 0xA80355534C61746Ellu, // dak_Latn_US
- 0xC40352554379726Cllu, // dar_Cyrl_RU
- 0xD4034B454C61746Ellu, // dav_Latn_KE
- 0x8843494E41726162llu, // dcc_Arab_IN
- 0x646544454C61746Ellu, // de_Latn_DE
- 0xB48343414C61746Ellu, // den_Latn_CA
- 0xC4C343414C61746Ellu, // dgr_Latn_CA
- 0x91234E454C61746Ellu, // dje_Latn_NE
- 0xA5A343494C61746Ellu, // dnj_Latn_CI
- 0xA1C3494E41726162llu, // doi_Arab_IN
- 0x864344454C61746Ellu, // dsb_Latn_DE
- 0xB2634D4C4C61746Ellu, // dtm_Latn_ML
- 0xBE634D594C61746Ellu, // dtp_Latn_MY
- 0xE2634E5044657661llu, // dty_Deva_NP
- 0x8283434D4C61746Ellu, // dua_Latn_CM
- 0x64764D5654686161llu, // dv_Thaa_MV
- 0xBB03534E4C61746Ellu, // dyo_Latn_SN
- 0xD30342464C61746Ellu, // dyu_Latn_BF
- 0x647A425454696274llu, // dz_Tibt_BT
- 0xD0244B454C61746Ellu, // ebu_Latn_KE
- 0x656547484C61746Ellu, // ee_Latn_GH
- 0xA0A44E474C61746Ellu, // efi_Latn_NG
- 0xACC449544C61746Ellu, // egl_Latn_IT
- 0xE0C4454745677970llu, // egy_Egyp_EG
- 0xE1444D4D4B616C69llu, // eky_Kali_MM
- 0x656C47524772656Bllu, // el_Grek_GR
- 0x656E47424C61746Ellu, // en_Latn_GB
- 0x656E55534C61746Ellu, // en_Latn_US
- 0x656E474253686177llu, // en_Shaw_GB
- 0x657345534C61746Ellu, // es_Latn_ES
- 0x65734D584C61746Ellu, // es_Latn_MX
- 0x657355534C61746Ellu, // es_Latn_US
- 0xD24455534C61746Ellu, // esu_Latn_US
- 0x657445454C61746Ellu, // et_Latn_EE
- 0xCE6449544974616Cllu, // ett_Ital_IT
- 0x657545534C61746Ellu, // eu_Latn_ES
- 0xBAC4434D4C61746Ellu, // ewo_Latn_CM
- 0xCEE445534C61746Ellu, // ext_Latn_ES
- 0x6661495241726162llu, // fa_Arab_IR
- 0xB40547514C61746Ellu, // fan_Latn_GQ
- 0x6666474E41646C6Dllu, // ff_Adlm_GN
- 0x6666534E4C61746Ellu, // ff_Latn_SN
- 0xB0A54D4C4C61746Ellu, // ffm_Latn_ML
- 0x666946494C61746Ellu, // fi_Latn_FI
- 0x8105534441726162llu, // fia_Arab_SD
- 0xAD0550484C61746Ellu, // fil_Latn_PH
- 0xCD0553454C61746Ellu, // fit_Latn_SE
- 0x666A464A4C61746Ellu, // fj_Latn_FJ
- 0x666F464F4C61746Ellu, // fo_Latn_FO
- 0xB5C5424A4C61746Ellu, // fon_Latn_BJ
- 0x667246524C61746Ellu, // fr_Latn_FR
- 0x8A2555534C61746Ellu, // frc_Latn_US
- 0xBE2546524C61746Ellu, // frp_Latn_FR
- 0xC62544454C61746Ellu, // frr_Latn_DE
- 0xCA2544454C61746Ellu, // frs_Latn_DE
- 0x8685434D41726162llu, // fub_Arab_CM
- 0x8E8557464C61746Ellu, // fud_Latn_WF
- 0x9685474E4C61746Ellu, // fuf_Latn_GN
- 0xC2854E454C61746Ellu, // fuq_Latn_NE
- 0xC68549544C61746Ellu, // fur_Latn_IT
- 0xD6854E474C61746Ellu, // fuv_Latn_NG
- 0xC6A553444C61746Ellu, // fvr_Latn_SD
- 0x66794E4C4C61746Ellu, // fy_Latn_NL
- 0x676149454C61746Ellu, // ga_Latn_IE
- 0x800647484C61746Ellu, // gaa_Latn_GH
- 0x98064D444C61746Ellu, // gag_Latn_MD
- 0xB406434E48616E73llu, // gan_Hans_CN
- 0xE00649444C61746Ellu, // gay_Latn_ID
- 0xB026494E44657661llu, // gbm_Deva_IN
- 0xE426495241726162llu, // gbz_Arab_IR
- 0xC44647464C61746Ellu, // gcr_Latn_GF
- 0x676447424C61746Ellu, // gd_Latn_GB
- 0xE486455445746869llu, // gez_Ethi_ET
- 0xB4C64E5044657661llu, // ggn_Deva_NP
- 0xAD064B494C61746Ellu, // gil_Latn_KI
- 0xA926504B41726162llu, // gjk_Arab_PK
- 0xD126504B41726162llu, // gju_Arab_PK
- 0x676C45534C61746Ellu, // gl_Latn_ES
- 0xA966495241726162llu, // glk_Arab_IR
- 0x676E50594C61746Ellu, // gn_Latn_PY
- 0xB1C6494E44657661llu, // gom_Deva_IN
- 0xB5C6494E54656C75llu, // gon_Telu_IN
- 0xC5C649444C61746Ellu, // gor_Latn_ID
- 0xC9C64E4C4C61746Ellu, // gos_Latn_NL
- 0xCDC65541476F7468llu, // got_Goth_UA
- 0x8A26435943707274llu, // grc_Cprt_CY
- 0x8A2647524C696E62llu, // grc_Linb_GR
- 0xCE26494E42656E67llu, // grt_Beng_IN
- 0xDA4643484C61746Ellu, // gsw_Latn_CH
- 0x6775494E47756A72llu, // gu_Gujr_IN
- 0x868642524C61746Ellu, // gub_Latn_BR
- 0x8A86434F4C61746Ellu, // guc_Latn_CO
- 0xC68647484C61746Ellu, // gur_Latn_GH
- 0xE6864B454C61746Ellu, // guz_Latn_KE
- 0x6776494D4C61746Ellu, // gv_Latn_IM
- 0xC6A64E5044657661llu, // gvr_Deva_NP
- 0xA2C643414C61746Ellu, // gwi_Latn_CA
- 0x68614E474C61746Ellu, // ha_Latn_NG
- 0xA807434E48616E73llu, // hak_Hans_CN
- 0xD80755534C61746Ellu, // haw_Latn_US
- 0xE407414641726162llu, // haz_Arab_AF
- 0x6865494C48656272llu, // he_Hebr_IL
- 0x6869494E44657661llu, // hi_Deva_IN
- 0x9507464A4C61746Ellu, // hif_Latn_FJ
- 0xAD0750484C61746Ellu, // hil_Latn_PH
- 0xD1675452486C7577llu, // hlu_Hluw_TR
- 0x8D87434E506C7264llu, // hmd_Plrd_CN
- 0x8DA7504B41726162llu, // hnd_Arab_PK
- 0x91A7494E44657661llu, // hne_Deva_IN
- 0xA5A74C41486D6E67llu, // hnj_Hmng_LA
- 0xB5A750484C61746Ellu, // hnn_Latn_PH
- 0xB9A7504B41726162llu, // hno_Arab_PK
- 0x686F50474C61746Ellu, // ho_Latn_PG
- 0x89C7494E44657661llu, // hoc_Deva_IN
- 0xA5C7494E44657661llu, // hoj_Deva_IN
- 0x687248524C61746Ellu, // hr_Latn_HR
- 0x864744454C61746Ellu, // hsb_Latn_DE
- 0xB647434E48616E73llu, // hsn_Hans_CN
- 0x687448544C61746Ellu, // ht_Latn_HT
- 0x687548554C61746Ellu, // hu_Latn_HU
- 0x6879414D41726D6Ellu, // hy_Armn_AM
- 0x687A4E414C61746Ellu, // hz_Latn_NA
- 0x696146524C61746Ellu, // ia_Latn_FR
- 0x80284D594C61746Ellu, // iba_Latn_MY
- 0x84284E474C61746Ellu, // ibb_Latn_NG
- 0x696449444C61746Ellu, // id_Latn_ID
- 0x69674E474C61746Ellu, // ig_Latn_NG
- 0x6969434E59696969llu, // ii_Yiii_CN
- 0x696B55534C61746Ellu, // ik_Latn_US
- 0xCD4843414C61746Ellu, // ikt_Latn_CA
- 0xB96850484C61746Ellu, // ilo_Latn_PH
- 0x696E49444C61746Ellu, // in_Latn_ID
- 0x9DA852554379726Cllu, // inh_Cyrl_RU
- 0x697349534C61746Ellu, // is_Latn_IS
- 0x697449544C61746Ellu, // it_Latn_IT
- 0x6975434143616E73llu, // iu_Cans_CA
- 0x6977494C48656272llu, // iw_Hebr_IL
- 0x9F2852554C61746Ellu, // izh_Latn_RU
- 0x6A614A504A70616Ellu, // ja_Jpan_JP
- 0xB0094A4D4C61746Ellu, // jam_Latn_JM
- 0xB8C9434D4C61746Ellu, // jgo_Latn_CM
- 0x8989545A4C61746Ellu, // jmc_Latn_TZ
- 0xAD894E5044657661llu, // jml_Deva_NP
- 0xCE89444B4C61746Ellu, // jut_Latn_DK
- 0x6A7649444C61746Ellu, // jv_Latn_ID
- 0x6A7749444C61746Ellu, // jw_Latn_ID
- 0x6B61474547656F72llu, // ka_Geor_GE
- 0x800A555A4379726Cllu, // kaa_Cyrl_UZ
- 0x840A445A4C61746Ellu, // kab_Latn_DZ
- 0x880A4D4D4C61746Ellu, // kac_Latn_MM
- 0xA40A4E474C61746Ellu, // kaj_Latn_NG
- 0xB00A4B454C61746Ellu, // kam_Latn_KE
- 0xB80A4D4C4C61746Ellu, // kao_Latn_ML
- 0x8C2A52554379726Cllu, // kbd_Cyrl_RU
- 0xE02A4E4541726162llu, // kby_Arab_NE
- 0x984A4E474C61746Ellu, // kcg_Latn_NG
- 0xA84A5A574C61746Ellu, // kck_Latn_ZW
- 0x906A545A4C61746Ellu, // kde_Latn_TZ
- 0x9C6A544741726162llu, // kdh_Arab_TG
- 0xCC6A544854686169llu, // kdt_Thai_TH
- 0x808A43564C61746Ellu, // kea_Latn_CV
- 0xB48A434D4C61746Ellu, // ken_Latn_CM
- 0xB8AA43494C61746Ellu, // kfo_Latn_CI
- 0xC4AA494E44657661llu, // kfr_Deva_IN
- 0xE0AA494E44657661llu, // kfy_Deva_IN
- 0x6B6743444C61746Ellu, // kg_Latn_CD
- 0x90CA49444C61746Ellu, // kge_Latn_ID
- 0xBCCA42524C61746Ellu, // kgp_Latn_BR
- 0x80EA494E4C61746Ellu, // kha_Latn_IN
- 0x84EA434E54616C75llu, // khb_Talu_CN
- 0xB4EA494E44657661llu, // khn_Deva_IN
- 0xC0EA4D4C4C61746Ellu, // khq_Latn_ML
- 0xCCEA494E4D796D72llu, // kht_Mymr_IN
- 0xD8EA504B41726162llu, // khw_Arab_PK
- 0x6B694B454C61746Ellu, // ki_Latn_KE
- 0xD10A54524C61746Ellu, // kiu_Latn_TR
- 0x6B6A4E414C61746Ellu, // kj_Latn_NA
- 0x992A4C414C616F6Fllu, // kjg_Laoo_LA
- 0x6B6B434E41726162llu, // kk_Arab_CN
- 0x6B6B4B5A4379726Cllu, // kk_Cyrl_KZ
- 0xA54A434D4C61746Ellu, // kkj_Latn_CM
- 0x6B6C474C4C61746Ellu, // kl_Latn_GL
- 0xB56A4B454C61746Ellu, // kln_Latn_KE
- 0x6B6D4B484B686D72llu, // km_Khmr_KH
- 0x858A414F4C61746Ellu, // kmb_Latn_AO
- 0x6B6E494E4B6E6461llu, // kn_Knda_IN
- 0x6B6F4B524B6F7265llu, // ko_Kore_KR
- 0xA1CA52554379726Cllu, // koi_Cyrl_RU
- 0xA9CA494E44657661llu, // kok_Deva_IN
- 0xC9CA464D4C61746Ellu, // kos_Latn_FM
- 0x91EA4C524C61746Ellu, // kpe_Latn_LR
- 0x8A2A52554379726Cllu, // krc_Cyrl_RU
- 0xA22A534C4C61746Ellu, // kri_Latn_SL
- 0xA62A50484C61746Ellu, // krj_Latn_PH
- 0xAE2A52554C61746Ellu, // krl_Latn_RU
- 0xD22A494E44657661llu, // kru_Deva_IN
- 0x6B73494E41726162llu, // ks_Arab_IN
- 0x864A545A4C61746Ellu, // ksb_Latn_TZ
- 0x964A434D4C61746Ellu, // ksf_Latn_CM
- 0x9E4A44454C61746Ellu, // ksh_Latn_DE
- 0x6B75495141726162llu, // ku_Arab_IQ
- 0x6B7554524C61746Ellu, // ku_Latn_TR
- 0xB28A52554379726Cllu, // kum_Cyrl_RU
- 0x6B7652554379726Cllu, // kv_Cyrl_RU
- 0xC6AA49444C61746Ellu, // kvr_Latn_ID
- 0xDEAA504B41726162llu, // kvx_Arab_PK
- 0x6B7747424C61746Ellu, // kw_Latn_GB
- 0xB2EA544854686169llu, // kxm_Thai_TH
- 0xBEEA504B41726162llu, // kxp_Arab_PK
- 0x6B79434E41726162llu, // ky_Arab_CN
- 0x6B794B474379726Cllu, // ky_Cyrl_KG
- 0x6B7954524C61746Ellu, // ky_Latn_TR
- 0x6C6156414C61746Ellu, // la_Latn_VA
- 0x840B47524C696E61llu, // lab_Lina_GR
- 0x8C0B494C48656272llu, // lad_Hebr_IL
- 0x980B545A4C61746Ellu, // lag_Latn_TZ
- 0x9C0B504B41726162llu, // lah_Arab_PK
- 0xA40B55474C61746Ellu, // laj_Latn_UG
- 0x6C624C554C61746Ellu, // lb_Latn_LU
- 0x902B52554379726Cllu, // lbe_Cyrl_RU
- 0xD82B49444C61746Ellu, // lbw_Latn_ID
- 0xBC4B434E54686169llu, // lcp_Thai_CN
- 0xBC8B494E4C657063llu, // lep_Lepc_IN
- 0xE48B52554379726Cllu, // lez_Cyrl_RU
- 0x6C6755474C61746Ellu, // lg_Latn_UG
- 0x6C694E4C4C61746Ellu, // li_Latn_NL
- 0x950B4E5044657661llu, // lif_Deva_NP
- 0x950B494E4C696D62llu, // lif_Limb_IN
- 0xA50B49544C61746Ellu, // lij_Latn_IT
- 0xC90B434E4C697375llu, // lis_Lisu_CN
- 0xBD2B49444C61746Ellu, // ljp_Latn_ID
- 0xA14B495241726162llu, // lki_Arab_IR
- 0xCD4B55534C61746Ellu, // lkt_Latn_US
- 0xB58B494E54656C75llu, // lmn_Telu_IN
- 0xB98B49544C61746Ellu, // lmo_Latn_IT
- 0x6C6E43444C61746Ellu, // ln_Latn_CD
- 0x6C6F4C414C616F6Fllu, // lo_Laoo_LA
- 0xADCB43444C61746Ellu, // lol_Latn_CD
- 0xE5CB5A4D4C61746Ellu, // loz_Latn_ZM
- 0x8A2B495241726162llu, // lrc_Arab_IR
- 0x6C744C544C61746Ellu, // lt_Latn_LT
- 0x9A6B4C564C61746Ellu, // ltg_Latn_LV
- 0x6C7543444C61746Ellu, // lu_Latn_CD
- 0x828B43444C61746Ellu, // lua_Latn_CD
- 0xBA8B4B454C61746Ellu, // luo_Latn_KE
- 0xE28B4B454C61746Ellu, // luy_Latn_KE
- 0xE68B495241726162llu, // luz_Arab_IR
- 0x6C764C564C61746Ellu, // lv_Latn_LV
- 0xAECB544854686169llu, // lwl_Thai_TH
- 0x9F2B434E48616E73llu, // lzh_Hans_CN
- 0xE72B54524C61746Ellu, // lzz_Latn_TR
- 0x8C0C49444C61746Ellu, // mad_Latn_ID
- 0x940C434D4C61746Ellu, // maf_Latn_CM
- 0x980C494E44657661llu, // mag_Deva_IN
- 0xA00C494E44657661llu, // mai_Deva_IN
- 0xA80C49444C61746Ellu, // mak_Latn_ID
- 0xB40C474D4C61746Ellu, // man_Latn_GM
- 0xB40C474E4E6B6F6Fllu, // man_Nkoo_GN
- 0xC80C4B454C61746Ellu, // mas_Latn_KE
- 0xE40C4D584C61746Ellu, // maz_Latn_MX
- 0x946C52554379726Cllu, // mdf_Cyrl_RU
- 0x9C6C50484C61746Ellu, // mdh_Latn_PH
- 0xC46C49444C61746Ellu, // mdr_Latn_ID
- 0xB48C534C4C61746Ellu, // men_Latn_SL
- 0xC48C4B454C61746Ellu, // mer_Latn_KE
- 0x80AC544841726162llu, // mfa_Arab_TH
- 0x90AC4D554C61746Ellu, // mfe_Latn_MU
- 0x6D674D474C61746Ellu, // mg_Latn_MG
- 0x9CCC4D5A4C61746Ellu, // mgh_Latn_MZ
- 0xB8CC434D4C61746Ellu, // mgo_Latn_CM
- 0xBCCC4E5044657661llu, // mgp_Deva_NP
- 0xE0CC545A4C61746Ellu, // mgy_Latn_TZ
- 0x6D684D484C61746Ellu, // mh_Latn_MH
- 0x6D694E5A4C61746Ellu, // mi_Latn_NZ
- 0xB50C49444C61746Ellu, // min_Latn_ID
- 0xC90C495148617472llu, // mis_Hatr_IQ
- 0x6D6B4D4B4379726Cllu, // mk_Cyrl_MK
- 0x6D6C494E4D6C796Dllu, // ml_Mlym_IN
- 0xC96C53444C61746Ellu, // mls_Latn_SD
- 0x6D6E4D4E4379726Cllu, // mn_Cyrl_MN
- 0x6D6E434E4D6F6E67llu, // mn_Mong_CN
- 0xA1AC494E42656E67llu, // mni_Beng_IN
- 0xD9AC4D4D4D796D72llu, // mnw_Mymr_MM
- 0x91CC43414C61746Ellu, // moe_Latn_CA
- 0x9DCC43414C61746Ellu, // moh_Latn_CA
- 0xC9CC42464C61746Ellu, // mos_Latn_BF
- 0x6D72494E44657661llu, // mr_Deva_IN
- 0x8E2C4E5044657661llu, // mrd_Deva_NP
- 0xA62C52554379726Cllu, // mrj_Cyrl_RU
- 0xBA2C42444D726F6Fllu, // mro_Mroo_BD
- 0x6D734D594C61746Ellu, // ms_Latn_MY
- 0x6D744D544C61746Ellu, // mt_Latn_MT
- 0xC66C494E44657661llu, // mtr_Deva_IN
- 0x828C434D4C61746Ellu, // mua_Latn_CM
- 0xCA8C55534C61746Ellu, // mus_Latn_US
- 0xE2AC504B41726162llu, // mvy_Arab_PK
- 0xAACC4D4C4C61746Ellu, // mwk_Latn_ML
- 0xC6CC494E44657661llu, // mwr_Deva_IN
- 0xD6CC49444C61746Ellu, // mwv_Latn_ID
- 0x8AEC5A574C61746Ellu, // mxc_Latn_ZW
- 0x6D794D4D4D796D72llu, // my_Mymr_MM
- 0xD70C52554379726Cllu, // myv_Cyrl_RU
- 0xDF0C55474C61746Ellu, // myx_Latn_UG
- 0xE70C49524D616E64llu, // myz_Mand_IR
- 0xB72C495241726162llu, // mzn_Arab_IR
- 0x6E614E524C61746Ellu, // na_Latn_NR
- 0xB40D434E48616E73llu, // nan_Hans_CN
- 0xBC0D49544C61746Ellu, // nap_Latn_IT
- 0xC00D4E414C61746Ellu, // naq_Latn_NA
- 0x6E624E4F4C61746Ellu, // nb_Latn_NO
- 0x9C4D4D584C61746Ellu, // nch_Latn_MX
- 0x6E645A574C61746Ellu, // nd_Latn_ZW
- 0x886D4D5A4C61746Ellu, // ndc_Latn_MZ
- 0xC86D44454C61746Ellu, // nds_Latn_DE
- 0x6E654E5044657661llu, // ne_Deva_NP
- 0xD88D4E5044657661llu, // new_Deva_NP
- 0x6E674E414C61746Ellu, // ng_Latn_NA
- 0xACCD4D5A4C61746Ellu, // ngl_Latn_MZ
- 0x90ED4D584C61746Ellu, // nhe_Latn_MX
- 0xD8ED4D584C61746Ellu, // nhw_Latn_MX
- 0xA50D49444C61746Ellu, // nij_Latn_ID
- 0xD10D4E554C61746Ellu, // niu_Latn_NU
- 0xB92D494E4C61746Ellu, // njo_Latn_IN
- 0x6E6C4E4C4C61746Ellu, // nl_Latn_NL
- 0x998D434D4C61746Ellu, // nmg_Latn_CM
- 0x6E6E4E4F4C61746Ellu, // nn_Latn_NO
- 0x9DAD434D4C61746Ellu, // nnh_Latn_CM
- 0x6E6F4E4F4C61746Ellu, // no_Latn_NO
- 0x8DCD54484C616E61llu, // nod_Lana_TH
- 0x91CD494E44657661llu, // noe_Deva_IN
- 0xB5CD534552756E72llu, // non_Runr_SE
- 0xBA0D474E4E6B6F6Fllu, // nqo_Nkoo_GN
- 0x6E725A414C61746Ellu, // nr_Latn_ZA
- 0xAA4D434143616E73llu, // nsk_Cans_CA
- 0xBA4D5A414C61746Ellu, // nso_Latn_ZA
- 0xCA8D53534C61746Ellu, // nus_Latn_SS
- 0x6E7655534C61746Ellu, // nv_Latn_US
- 0xC2ED434E4C61746Ellu, // nxq_Latn_CN
- 0x6E794D574C61746Ellu, // ny_Latn_MW
- 0xB30D545A4C61746Ellu, // nym_Latn_TZ
- 0xB70D55474C61746Ellu, // nyn_Latn_UG
- 0xA32D47484C61746Ellu, // nzi_Latn_GH
- 0x6F6346524C61746Ellu, // oc_Latn_FR
- 0x6F6D45544C61746Ellu, // om_Latn_ET
- 0x6F72494E4F727961llu, // or_Orya_IN
- 0x6F7347454379726Cllu, // os_Cyrl_GE
- 0x824E55534F736765llu, // osa_Osge_US
- 0xAA6E4D4E4F726B68llu, // otk_Orkh_MN
- 0x7061504B41726162llu, // pa_Arab_PK
- 0x7061494E47757275llu, // pa_Guru_IN
- 0x980F50484C61746Ellu, // pag_Latn_PH
- 0xAC0F495250686C69llu, // pal_Phli_IR
- 0xAC0F434E50686C70llu, // pal_Phlp_CN
- 0xB00F50484C61746Ellu, // pam_Latn_PH
- 0xBC0F41574C61746Ellu, // pap_Latn_AW
- 0xD00F50574C61746Ellu, // pau_Latn_PW
- 0x8C4F46524C61746Ellu, // pcd_Latn_FR
- 0xB04F4E474C61746Ellu, // pcm_Latn_NG
- 0x886F55534C61746Ellu, // pdc_Latn_US
- 0xCC6F43414C61746Ellu, // pdt_Latn_CA
- 0xB88F49525870656Fllu, // peo_Xpeo_IR
- 0xACAF44454C61746Ellu, // pfl_Latn_DE
- 0xB4EF4C4250686E78llu, // phn_Phnx_LB
- 0x814F494E42726168llu, // pka_Brah_IN
- 0xB94F4B454C61746Ellu, // pko_Latn_KE
- 0x706C504C4C61746Ellu, // pl_Latn_PL
- 0xC98F49544C61746Ellu, // pms_Latn_IT
- 0xCDAF47524772656Bllu, // pnt_Grek_GR
- 0xB5CF464D4C61746Ellu, // pon_Latn_FM
- 0x822F504B4B686172llu, // pra_Khar_PK
- 0x8E2F495241726162llu, // prd_Arab_IR
- 0x7073414641726162llu, // ps_Arab_AF
- 0x707442524C61746Ellu, // pt_Latn_BR
- 0xD28F47414C61746Ellu, // puu_Latn_GA
- 0x717550454C61746Ellu, // qu_Latn_PE
- 0x8A9047544C61746Ellu, // quc_Latn_GT
- 0x9A9045434C61746Ellu, // qug_Latn_EC
- 0xA411494E44657661llu, // raj_Deva_IN
- 0x945152454C61746Ellu, // rcf_Latn_RE
- 0xA49149444C61746Ellu, // rej_Latn_ID
- 0xB4D149544C61746Ellu, // rgn_Latn_IT
- 0x8111494E4C61746Ellu, // ria_Latn_IN
- 0x95114D4154666E67llu, // rif_Tfng_MA
- 0xC9314E5044657661llu, // rjs_Deva_NP
- 0xCD51424442656E67llu, // rkt_Beng_BD
- 0x726D43484C61746Ellu, // rm_Latn_CH
- 0x959146494C61746Ellu, // rmf_Latn_FI
- 0xB99143484C61746Ellu, // rmo_Latn_CH
- 0xCD91495241726162llu, // rmt_Arab_IR
- 0xD19153454C61746Ellu, // rmu_Latn_SE
- 0x726E42494C61746Ellu, // rn_Latn_BI
- 0x99B14D5A4C61746Ellu, // rng_Latn_MZ
- 0x726F524F4C61746Ellu, // ro_Latn_RO
- 0x85D149444C61746Ellu, // rob_Latn_ID
- 0x95D1545A4C61746Ellu, // rof_Latn_TZ
- 0xB271464A4C61746Ellu, // rtm_Latn_FJ
- 0x727552554379726Cllu, // ru_Cyrl_RU
- 0x929155414379726Cllu, // rue_Cyrl_UA
- 0x9A9153424C61746Ellu, // rug_Latn_SB
- 0x727752574C61746Ellu, // rw_Latn_RW
- 0xAAD1545A4C61746Ellu, // rwk_Latn_TZ
- 0xD3114A504B616E61llu, // ryu_Kana_JP
- 0x7361494E44657661llu, // sa_Deva_IN
- 0x941247484C61746Ellu, // saf_Latn_GH
- 0x9C1252554379726Cllu, // sah_Cyrl_RU
- 0xC0124B454C61746Ellu, // saq_Latn_KE
- 0xC81249444C61746Ellu, // sas_Latn_ID
- 0xCC12494E4C61746Ellu, // sat_Latn_IN
- 0xE412494E53617572llu, // saz_Saur_IN
- 0xBC32545A4C61746Ellu, // sbp_Latn_TZ
- 0x736349544C61746Ellu, // sc_Latn_IT
- 0xA852494E44657661llu, // sck_Deva_IN
- 0xB45249544C61746Ellu, // scn_Latn_IT
- 0xB85247424C61746Ellu, // sco_Latn_GB
- 0xC85243414C61746Ellu, // scs_Latn_CA
- 0x7364504B41726162llu, // sd_Arab_PK
- 0x7364494E44657661llu, // sd_Deva_IN
- 0x7364494E4B686F6Allu, // sd_Khoj_IN
- 0x7364494E53696E64llu, // sd_Sind_IN
- 0x887249544C61746Ellu, // sdc_Latn_IT
- 0x9C72495241726162llu, // sdh_Arab_IR
- 0x73654E4F4C61746Ellu, // se_Latn_NO
- 0x949243494C61746Ellu, // sef_Latn_CI
- 0x9C924D5A4C61746Ellu, // seh_Latn_MZ
- 0xA0924D584C61746Ellu, // sei_Latn_MX
- 0xC8924D4C4C61746Ellu, // ses_Latn_ML
- 0x736743464C61746Ellu, // sg_Latn_CF
- 0x80D249454F67616Dllu, // sga_Ogam_IE
- 0xC8D24C544C61746Ellu, // sgs_Latn_LT
- 0xA0F24D4154666E67llu, // shi_Tfng_MA
- 0xB4F24D4D4D796D72llu, // shn_Mymr_MM
- 0x73694C4B53696E68llu, // si_Sinh_LK
- 0x8D1245544C61746Ellu, // sid_Latn_ET
- 0x736B534B4C61746Ellu, // sk_Latn_SK
- 0xC552504B41726162llu, // skr_Arab_PK
- 0x736C53494C61746Ellu, // sl_Latn_SI
- 0xA172504C4C61746Ellu, // sli_Latn_PL
- 0xE17249444C61746Ellu, // sly_Latn_ID
- 0x736D57534C61746Ellu, // sm_Latn_WS
- 0x819253454C61746Ellu, // sma_Latn_SE
- 0xA59253454C61746Ellu, // smj_Latn_SE
- 0xB59246494C61746Ellu, // smn_Latn_FI
- 0xBD92494C53616D72llu, // smp_Samr_IL
- 0xC99246494C61746Ellu, // sms_Latn_FI
- 0x736E5A574C61746Ellu, // sn_Latn_ZW
- 0xA9B24D4C4C61746Ellu, // snk_Latn_ML
- 0x736F534F4C61746Ellu, // so_Latn_SO
- 0xD1D2544854686169llu, // sou_Thai_TH
- 0x7371414C4C61746Ellu, // sq_Latn_AL
- 0x737252534379726Cllu, // sr_Cyrl_RS
- 0x737252534C61746Ellu, // sr_Latn_RS
- 0x8632494E536F7261llu, // srb_Sora_IN
- 0xB63253524C61746Ellu, // srn_Latn_SR
- 0xC632534E4C61746Ellu, // srr_Latn_SN
- 0xDE32494E44657661llu, // srx_Deva_IN
- 0x73735A414C61746Ellu, // ss_Latn_ZA
- 0xE25245524C61746Ellu, // ssy_Latn_ER
- 0x73745A414C61746Ellu, // st_Latn_ZA
- 0xC27244454C61746Ellu, // stq_Latn_DE
- 0x737549444C61746Ellu, // su_Latn_ID
- 0xAA92545A4C61746Ellu, // suk_Latn_TZ
- 0xCA92474E4C61746Ellu, // sus_Latn_GN
- 0x737653454C61746Ellu, // sv_Latn_SE
- 0x7377545A4C61746Ellu, // sw_Latn_TZ
- 0x86D2595441726162llu, // swb_Arab_YT
- 0x8AD243444C61746Ellu, // swc_Latn_CD
- 0x9AD244454C61746Ellu, // swg_Latn_DE
- 0xD6D2494E44657661llu, // swv_Deva_IN
- 0xB6F249444C61746Ellu, // sxn_Latn_ID
- 0xAF12424442656E67llu, // syl_Beng_BD
- 0xC712495153797263llu, // syr_Syrc_IQ
- 0xAF32504C4C61746Ellu, // szl_Latn_PL
- 0x7461494E54616D6Cllu, // ta_Taml_IN
- 0xA4134E5044657661llu, // taj_Deva_NP
- 0xD83350484C61746Ellu, // tbw_Latn_PH
- 0xE053494E4B6E6461llu, // tcy_Knda_IN
- 0x8C73434E54616C65llu, // tdd_Tale_CN
- 0x98734E5044657661llu, // tdg_Deva_NP
- 0x9C734E5044657661llu, // tdh_Deva_NP
- 0x7465494E54656C75llu, // te_Telu_IN
- 0xB093534C4C61746Ellu, // tem_Latn_SL
- 0xB89355474C61746Ellu, // teo_Latn_UG
- 0xCC93544C4C61746Ellu, // tet_Latn_TL
- 0x7467504B41726162llu, // tg_Arab_PK
- 0x7467544A4379726Cllu, // tg_Cyrl_TJ
- 0x7468544854686169llu, // th_Thai_TH
- 0xACF34E5044657661llu, // thl_Deva_NP
- 0xC0F34E5044657661llu, // thq_Deva_NP
- 0xC4F34E5044657661llu, // thr_Deva_NP
- 0x7469455445746869llu, // ti_Ethi_ET
- 0x9913455245746869llu, // tig_Ethi_ER
- 0xD5134E474C61746Ellu, // tiv_Latn_NG
- 0x746B544D4C61746Ellu, // tk_Latn_TM
- 0xAD53544B4C61746Ellu, // tkl_Latn_TK
- 0xC553415A4C61746Ellu, // tkr_Latn_AZ
- 0xCD534E5044657661llu, // tkt_Deva_NP
- 0x746C50484C61746Ellu, // tl_Latn_PH
- 0xE173415A4C61746Ellu, // tly_Latn_AZ
- 0x9D934E454C61746Ellu, // tmh_Latn_NE
- 0x746E5A414C61746Ellu, // tn_Latn_ZA
- 0x746F544F4C61746Ellu, // to_Latn_TO
- 0x99D34D574C61746Ellu, // tog_Latn_MW
- 0xA1F350474C61746Ellu, // tpi_Latn_PG
- 0x747254524C61746Ellu, // tr_Latn_TR
- 0xD23354524C61746Ellu, // tru_Latn_TR
- 0xD63354574C61746Ellu, // trv_Latn_TW
- 0x74735A414C61746Ellu, // ts_Latn_ZA
- 0x8E5347524772656Bllu, // tsd_Grek_GR
- 0x96534E5044657661llu, // tsf_Deva_NP
- 0x9A5350484C61746Ellu, // tsg_Latn_PH
- 0xA653425454696274llu, // tsj_Tibt_BT
- 0x747452554379726Cllu, // tt_Cyrl_RU
- 0xA67355474C61746Ellu, // ttj_Latn_UG
- 0xCA73544854686169llu, // tts_Thai_TH
- 0xCE73415A4C61746Ellu, // ttt_Latn_AZ
- 0xB2934D574C61746Ellu, // tum_Latn_MW
- 0xAEB354564C61746Ellu, // tvl_Latn_TV
- 0xC2D34E454C61746Ellu, // twq_Latn_NE
- 0x9AF3434E54616E67llu, // txg_Tang_CN
- 0x747950464C61746Ellu, // ty_Latn_PF
- 0xD71352554379726Cllu, // tyv_Cyrl_RU
- 0xB3334D414C61746Ellu, // tzm_Latn_MA
- 0xB07452554379726Cllu, // udm_Cyrl_RU
- 0x7567434E41726162llu, // ug_Arab_CN
- 0x75674B5A4379726Cllu, // ug_Cyrl_KZ
- 0x80D4535955676172llu, // uga_Ugar_SY
- 0x756B55414379726Cllu, // uk_Cyrl_UA
- 0xA174464D4C61746Ellu, // uli_Latn_FM
- 0x8594414F4C61746Ellu, // umb_Latn_AO
- 0xC5B4494E42656E67llu, // unr_Beng_IN
- 0xC5B44E5044657661llu, // unr_Deva_NP
- 0xDDB4494E42656E67llu, // unx_Beng_IN
- 0x7572504B41726162llu, // ur_Arab_PK
- 0x757A414641726162llu, // uz_Arab_AF
- 0x757A555A4C61746Ellu, // uz_Latn_UZ
- 0xA0154C5256616969llu, // vai_Vaii_LR
- 0x76655A414C61746Ellu, // ve_Latn_ZA
- 0x889549544C61746Ellu, // vec_Latn_IT
- 0xBC9552554C61746Ellu, // vep_Latn_RU
- 0x7669564E4C61746Ellu, // vi_Latn_VN
- 0x891553584C61746Ellu, // vic_Latn_SX
- 0xC97542454C61746Ellu, // vls_Latn_BE
- 0x959544454C61746Ellu, // vmf_Latn_DE
- 0xD9954D5A4C61746Ellu, // vmw_Latn_MZ
- 0xCDD552554C61746Ellu, // vot_Latn_RU
- 0xBA3545454C61746Ellu, // vro_Latn_EE
- 0xB695545A4C61746Ellu, // vun_Latn_TZ
- 0x776142454C61746Ellu, // wa_Latn_BE
- 0x901643484C61746Ellu, // wae_Latn_CH
- 0xAC16455445746869llu, // wal_Ethi_ET
- 0xC41650484C61746Ellu, // war_Latn_PH
- 0xBC3641554C61746Ellu, // wbp_Latn_AU
- 0xC036494E54656C75llu, // wbq_Telu_IN
- 0xC436494E44657661llu, // wbr_Deva_IN
- 0xC97657464C61746Ellu, // wls_Latn_WF
- 0xA1B64B4D41726162llu, // wni_Arab_KM
- 0x776F534E4C61746Ellu, // wo_Latn_SN
- 0xB276494E44657661llu, // wtm_Deva_IN
- 0xD296434E48616E73llu, // wuu_Hans_CN
- 0xD41742524C61746Ellu, // xav_Latn_BR
- 0xC457545243617269llu, // xcr_Cari_TR
- 0x78685A414C61746Ellu, // xh_Latn_ZA
- 0x897754524C796369llu, // xlc_Lyci_TR
- 0x8D7754524C796469llu, // xld_Lydi_TR
- 0x9597474547656F72llu, // xmf_Geor_GE
- 0xB597434E4D616E69llu, // xmn_Mani_CN
- 0xC59753444D657263llu, // xmr_Merc_SD
- 0x81B753414E617262llu, // xna_Narb_SA
- 0xC5B7494E44657661llu, // xnr_Deva_IN
- 0x99D755474C61746Ellu, // xog_Latn_UG
- 0xC5F7495250727469llu, // xpr_Prti_IR
- 0x8257594553617262llu, // xsa_Sarb_YE
- 0xC6574E5044657661llu, // xsr_Deva_NP
- 0xB8184D5A4C61746Ellu, // yao_Latn_MZ
- 0xBC18464D4C61746Ellu, // yap_Latn_FM
- 0xD418434D4C61746Ellu, // yav_Latn_CM
- 0x8438434D4C61746Ellu, // ybb_Latn_CM
- 0x796F4E474C61746Ellu, // yo_Latn_NG
- 0xAE3842524C61746Ellu, // yrl_Latn_BR
- 0x82984D584C61746Ellu, // yua_Latn_MX
- 0x9298434E48616E73llu, // yue_Hans_CN
- 0x9298484B48616E74llu, // yue_Hant_HK
- 0x7A61434E4C61746Ellu, // za_Latn_CN
- 0x981953444C61746Ellu, // zag_Latn_SD
- 0xA4794B4D41726162llu, // zdj_Arab_KM
- 0x80994E4C4C61746Ellu, // zea_Latn_NL
- 0x9CD94D4154666E67llu, // zgh_Tfng_MA
- 0x7A685457426F706Fllu, // zh_Bopo_TW
- 0x7A68545748616E62llu, // zh_Hanb_TW
- 0x7A68434E48616E73llu, // zh_Hans_CN
- 0x7A68545748616E74llu, // zh_Hant_TW
- 0xB17954474C61746Ellu, // zlm_Latn_TG
- 0xA1994D594C61746Ellu, // zmi_Latn_MY
- 0x7A755A414C61746Ellu, // zu_Latn_ZA
- 0x833954524C61746Ellu, // zza_Latn_TR
+ 0x616145544C61746ELLU, // aa_Latn_ET
+ 0x616247454379726CLLU, // ab_Cyrl_GE
+ 0xC42047484C61746ELLU, // abr_Latn_GH
+ 0x904049444C61746ELLU, // ace_Latn_ID
+ 0x9C4055474C61746ELLU, // ach_Latn_UG
+ 0x806047484C61746ELLU, // ada_Latn_GH
+ 0xE06052554379726CLLU, // ady_Cyrl_RU
+ 0x6165495241767374LLU, // ae_Avst_IR
+ 0x8480544E41726162LLU, // aeb_Arab_TN
+ 0x61665A414C61746ELLU, // af_Latn_ZA
+ 0xC0C0434D4C61746ELLU, // agq_Latn_CM
+ 0xB8E0494E41686F6DLLU, // aho_Ahom_IN
+ 0x616B47484C61746ELLU, // ak_Latn_GH
+ 0xA940495158737578LLU, // akk_Xsux_IQ
+ 0xB560584B4C61746ELLU, // aln_Latn_XK
+ 0xCD6052554379726CLLU, // alt_Cyrl_RU
+ 0x616D455445746869LLU, // am_Ethi_ET
+ 0xB9804E474C61746ELLU, // amo_Latn_NG
+ 0xE5C049444C61746ELLU, // aoz_Latn_ID
+ 0x8DE0544741726162LLU, // apd_Arab_TG
+ 0x6172454741726162LLU, // ar_Arab_EG
+ 0x8A20495241726D69LLU, // arc_Armi_IR
+ 0x8A204A4F4E626174LLU, // arc_Nbat_JO
+ 0x8A20535950616C6DLLU, // arc_Palm_SY
+ 0xB620434C4C61746ELLU, // arn_Latn_CL
+ 0xBA20424F4C61746ELLU, // aro_Latn_BO
+ 0xC220445A41726162LLU, // arq_Arab_DZ
+ 0xE2204D4141726162LLU, // ary_Arab_MA
+ 0xE620454741726162LLU, // arz_Arab_EG
+ 0x6173494E42656E67LLU, // as_Beng_IN
+ 0x8240545A4C61746ELLU, // asa_Latn_TZ
+ 0x9240555353676E77LLU, // ase_Sgnw_US
+ 0xCE4045534C61746ELLU, // ast_Latn_ES
+ 0xA66043414C61746ELLU, // atj_Latn_CA
+ 0x617652554379726CLLU, // av_Cyrl_RU
+ 0x82C0494E44657661LLU, // awa_Deva_IN
+ 0x6179424F4C61746ELLU, // ay_Latn_BO
+ 0x617A495241726162LLU, // az_Arab_IR
+ 0x617A415A4C61746ELLU, // az_Latn_AZ
+ 0x626152554379726CLLU, // ba_Cyrl_RU
+ 0xAC01504B41726162LLU, // bal_Arab_PK
+ 0xB40149444C61746ELLU, // ban_Latn_ID
+ 0xBC014E5044657661LLU, // bap_Deva_NP
+ 0xC40141544C61746ELLU, // bar_Latn_AT
+ 0xC801434D4C61746ELLU, // bas_Latn_CM
+ 0xDC01434D42616D75LLU, // bax_Bamu_CM
+ 0x882149444C61746ELLU, // bbc_Latn_ID
+ 0xA421434D4C61746ELLU, // bbj_Latn_CM
+ 0xA04143494C61746ELLU, // bci_Latn_CI
+ 0x626542594379726CLLU, // be_Cyrl_BY
+ 0xA481534441726162LLU, // bej_Arab_SD
+ 0xB0815A4D4C61746ELLU, // bem_Latn_ZM
+ 0xD88149444C61746ELLU, // bew_Latn_ID
+ 0xE481545A4C61746ELLU, // bez_Latn_TZ
+ 0x8CA1434D4C61746ELLU, // bfd_Latn_CM
+ 0xC0A1494E54616D6CLLU, // bfq_Taml_IN
+ 0xCCA1504B41726162LLU, // bft_Arab_PK
+ 0xE0A1494E44657661LLU, // bfy_Deva_IN
+ 0x626742474379726CLLU, // bg_Cyrl_BG
+ 0x88C1494E44657661LLU, // bgc_Deva_IN
+ 0xB4C1504B41726162LLU, // bgn_Arab_PK
+ 0xDCC154524772656BLLU, // bgx_Grek_TR
+ 0x84E1494E44657661LLU, // bhb_Deva_IN
+ 0xA0E1494E44657661LLU, // bhi_Deva_IN
+ 0xA8E150484C61746ELLU, // bhk_Latn_PH
+ 0xB8E1494E44657661LLU, // bho_Deva_IN
+ 0x626956554C61746ELLU, // bi_Latn_VU
+ 0xA90150484C61746ELLU, // bik_Latn_PH
+ 0xB5014E474C61746ELLU, // bin_Latn_NG
+ 0xA521494E44657661LLU, // bjj_Deva_IN
+ 0xB52149444C61746ELLU, // bjn_Latn_ID
+ 0xB141434D4C61746ELLU, // bkm_Latn_CM
+ 0xD14150484C61746ELLU, // bku_Latn_PH
+ 0xCD61564E54617674LLU, // blt_Tavt_VN
+ 0x626D4D4C4C61746ELLU, // bm_Latn_ML
+ 0xC1814D4C4C61746ELLU, // bmq_Latn_ML
+ 0x626E424442656E67LLU, // bn_Beng_BD
+ 0x626F434E54696274LLU, // bo_Tibt_CN
+ 0xE1E1494E42656E67LLU, // bpy_Beng_IN
+ 0xA201495241726162LLU, // bqi_Arab_IR
+ 0xD60143494C61746ELLU, // bqv_Latn_CI
+ 0x627246524C61746ELLU, // br_Latn_FR
+ 0x8221494E44657661LLU, // bra_Deva_IN
+ 0x9E21504B41726162LLU, // brh_Arab_PK
+ 0xDE21494E44657661LLU, // brx_Deva_IN
+ 0x627342414C61746ELLU, // bs_Latn_BA
+ 0xC2414C5242617373LLU, // bsq_Bass_LR
+ 0xCA41434D4C61746ELLU, // bss_Latn_CM
+ 0xBA6150484C61746ELLU, // bto_Latn_PH
+ 0xD661504B44657661LLU, // btv_Deva_PK
+ 0x828152554379726CLLU, // bua_Cyrl_RU
+ 0x8A8159544C61746ELLU, // buc_Latn_YT
+ 0x9A8149444C61746ELLU, // bug_Latn_ID
+ 0xB281434D4C61746ELLU, // bum_Latn_CM
+ 0x86A147514C61746ELLU, // bvb_Latn_GQ
+ 0xB701455245746869LLU, // byn_Ethi_ER
+ 0xD701434D4C61746ELLU, // byv_Latn_CM
+ 0x93214D4C4C61746ELLU, // bze_Latn_ML
+ 0x636145534C61746ELLU, // ca_Latn_ES
+ 0x9C424E474C61746ELLU, // cch_Latn_NG
+ 0xBC42494E42656E67LLU, // ccp_Beng_IN
+ 0xBC42424443616B6DLLU, // ccp_Cakm_BD
+ 0x636552554379726CLLU, // ce_Cyrl_RU
+ 0x848250484C61746ELLU, // ceb_Latn_PH
+ 0x98C255474C61746ELLU, // cgg_Latn_UG
+ 0x636847554C61746ELLU, // ch_Latn_GU
+ 0xA8E2464D4C61746ELLU, // chk_Latn_FM
+ 0xB0E252554379726CLLU, // chm_Cyrl_RU
+ 0xB8E255534C61746ELLU, // cho_Latn_US
+ 0xBCE243414C61746ELLU, // chp_Latn_CA
+ 0xC4E2555343686572LLU, // chr_Cher_US
+ 0x81224B4841726162LLU, // cja_Arab_KH
+ 0xB122564E4368616DLLU, // cjm_Cham_VN
+ 0x8542495141726162LLU, // ckb_Arab_IQ
+ 0x636F46524C61746ELLU, // co_Latn_FR
+ 0xBDC24547436F7074LLU, // cop_Copt_EG
+ 0xC9E250484C61746ELLU, // cps_Latn_PH
+ 0x6372434143616E73LLU, // cr_Cans_CA
+ 0xA622434143616E73LLU, // crj_Cans_CA
+ 0xAA22434143616E73LLU, // crk_Cans_CA
+ 0xAE22434143616E73LLU, // crl_Cans_CA
+ 0xB222434143616E73LLU, // crm_Cans_CA
+ 0xCA2253434C61746ELLU, // crs_Latn_SC
+ 0x6373435A4C61746ELLU, // cs_Latn_CZ
+ 0x8642504C4C61746ELLU, // csb_Latn_PL
+ 0xDA42434143616E73LLU, // csw_Cans_CA
+ 0x8E624D4D50617563LLU, // ctd_Pauc_MM
+ 0x637552554379726CLLU, // cu_Cyrl_RU
+ 0x63754247476C6167LLU, // cu_Glag_BG
+ 0x637652554379726CLLU, // cv_Cyrl_RU
+ 0x637947424C61746ELLU, // cy_Latn_GB
+ 0x6461444B4C61746ELLU, // da_Latn_DK
+ 0xA80355534C61746ELLU, // dak_Latn_US
+ 0xC40352554379726CLLU, // dar_Cyrl_RU
+ 0xD4034B454C61746ELLU, // dav_Latn_KE
+ 0x8843494E41726162LLU, // dcc_Arab_IN
+ 0x646544454C61746ELLU, // de_Latn_DE
+ 0xB48343414C61746ELLU, // den_Latn_CA
+ 0xC4C343414C61746ELLU, // dgr_Latn_CA
+ 0x91234E454C61746ELLU, // dje_Latn_NE
+ 0xA5A343494C61746ELLU, // dnj_Latn_CI
+ 0xA1C3494E41726162LLU, // doi_Arab_IN
+ 0x864344454C61746ELLU, // dsb_Latn_DE
+ 0xB2634D4C4C61746ELLU, // dtm_Latn_ML
+ 0xBE634D594C61746ELLU, // dtp_Latn_MY
+ 0xE2634E5044657661LLU, // dty_Deva_NP
+ 0x8283434D4C61746ELLU, // dua_Latn_CM
+ 0x64764D5654686161LLU, // dv_Thaa_MV
+ 0xBB03534E4C61746ELLU, // dyo_Latn_SN
+ 0xD30342464C61746ELLU, // dyu_Latn_BF
+ 0x647A425454696274LLU, // dz_Tibt_BT
+ 0xD0244B454C61746ELLU, // ebu_Latn_KE
+ 0x656547484C61746ELLU, // ee_Latn_GH
+ 0xA0A44E474C61746ELLU, // efi_Latn_NG
+ 0xACC449544C61746ELLU, // egl_Latn_IT
+ 0xE0C4454745677970LLU, // egy_Egyp_EG
+ 0xE1444D4D4B616C69LLU, // eky_Kali_MM
+ 0x656C47524772656BLLU, // el_Grek_GR
+ 0x656E47424C61746ELLU, // en_Latn_GB
+ 0x656E55534C61746ELLU, // en_Latn_US
+ 0x656E474253686177LLU, // en_Shaw_GB
+ 0x657345534C61746ELLU, // es_Latn_ES
+ 0x65734D584C61746ELLU, // es_Latn_MX
+ 0x657355534C61746ELLU, // es_Latn_US
+ 0xD24455534C61746ELLU, // esu_Latn_US
+ 0x657445454C61746ELLU, // et_Latn_EE
+ 0xCE6449544974616CLLU, // ett_Ital_IT
+ 0x657545534C61746ELLU, // eu_Latn_ES
+ 0xBAC4434D4C61746ELLU, // ewo_Latn_CM
+ 0xCEE445534C61746ELLU, // ext_Latn_ES
+ 0x6661495241726162LLU, // fa_Arab_IR
+ 0xB40547514C61746ELLU, // fan_Latn_GQ
+ 0x6666474E41646C6DLLU, // ff_Adlm_GN
+ 0x6666534E4C61746ELLU, // ff_Latn_SN
+ 0xB0A54D4C4C61746ELLU, // ffm_Latn_ML
+ 0x666946494C61746ELLU, // fi_Latn_FI
+ 0x8105534441726162LLU, // fia_Arab_SD
+ 0xAD0550484C61746ELLU, // fil_Latn_PH
+ 0xCD0553454C61746ELLU, // fit_Latn_SE
+ 0x666A464A4C61746ELLU, // fj_Latn_FJ
+ 0x666F464F4C61746ELLU, // fo_Latn_FO
+ 0xB5C5424A4C61746ELLU, // fon_Latn_BJ
+ 0x667246524C61746ELLU, // fr_Latn_FR
+ 0x8A2555534C61746ELLU, // frc_Latn_US
+ 0xBE2546524C61746ELLU, // frp_Latn_FR
+ 0xC62544454C61746ELLU, // frr_Latn_DE
+ 0xCA2544454C61746ELLU, // frs_Latn_DE
+ 0x8685434D41726162LLU, // fub_Arab_CM
+ 0x8E8557464C61746ELLU, // fud_Latn_WF
+ 0x9685474E4C61746ELLU, // fuf_Latn_GN
+ 0xC2854E454C61746ELLU, // fuq_Latn_NE
+ 0xC68549544C61746ELLU, // fur_Latn_IT
+ 0xD6854E474C61746ELLU, // fuv_Latn_NG
+ 0xC6A553444C61746ELLU, // fvr_Latn_SD
+ 0x66794E4C4C61746ELLU, // fy_Latn_NL
+ 0x676149454C61746ELLU, // ga_Latn_IE
+ 0x800647484C61746ELLU, // gaa_Latn_GH
+ 0x98064D444C61746ELLU, // gag_Latn_MD
+ 0xB406434E48616E73LLU, // gan_Hans_CN
+ 0xE00649444C61746ELLU, // gay_Latn_ID
+ 0xB026494E44657661LLU, // gbm_Deva_IN
+ 0xE426495241726162LLU, // gbz_Arab_IR
+ 0xC44647464C61746ELLU, // gcr_Latn_GF
+ 0x676447424C61746ELLU, // gd_Latn_GB
+ 0xE486455445746869LLU, // gez_Ethi_ET
+ 0xB4C64E5044657661LLU, // ggn_Deva_NP
+ 0xAD064B494C61746ELLU, // gil_Latn_KI
+ 0xA926504B41726162LLU, // gjk_Arab_PK
+ 0xD126504B41726162LLU, // gju_Arab_PK
+ 0x676C45534C61746ELLU, // gl_Latn_ES
+ 0xA966495241726162LLU, // glk_Arab_IR
+ 0x676E50594C61746ELLU, // gn_Latn_PY
+ 0xB1C6494E44657661LLU, // gom_Deva_IN
+ 0xB5C6494E54656C75LLU, // gon_Telu_IN
+ 0xC5C649444C61746ELLU, // gor_Latn_ID
+ 0xC9C64E4C4C61746ELLU, // gos_Latn_NL
+ 0xCDC65541476F7468LLU, // got_Goth_UA
+ 0x8A26435943707274LLU, // grc_Cprt_CY
+ 0x8A2647524C696E62LLU, // grc_Linb_GR
+ 0xCE26494E42656E67LLU, // grt_Beng_IN
+ 0xDA4643484C61746ELLU, // gsw_Latn_CH
+ 0x6775494E47756A72LLU, // gu_Gujr_IN
+ 0x868642524C61746ELLU, // gub_Latn_BR
+ 0x8A86434F4C61746ELLU, // guc_Latn_CO
+ 0xC68647484C61746ELLU, // gur_Latn_GH
+ 0xE6864B454C61746ELLU, // guz_Latn_KE
+ 0x6776494D4C61746ELLU, // gv_Latn_IM
+ 0xC6A64E5044657661LLU, // gvr_Deva_NP
+ 0xA2C643414C61746ELLU, // gwi_Latn_CA
+ 0x68614E474C61746ELLU, // ha_Latn_NG
+ 0xA807434E48616E73LLU, // hak_Hans_CN
+ 0xD80755534C61746ELLU, // haw_Latn_US
+ 0xE407414641726162LLU, // haz_Arab_AF
+ 0x6865494C48656272LLU, // he_Hebr_IL
+ 0x6869494E44657661LLU, // hi_Deva_IN
+ 0x9507464A4C61746ELLU, // hif_Latn_FJ
+ 0xAD0750484C61746ELLU, // hil_Latn_PH
+ 0xD1675452486C7577LLU, // hlu_Hluw_TR
+ 0x8D87434E506C7264LLU, // hmd_Plrd_CN
+ 0x8DA7504B41726162LLU, // hnd_Arab_PK
+ 0x91A7494E44657661LLU, // hne_Deva_IN
+ 0xA5A74C41486D6E67LLU, // hnj_Hmng_LA
+ 0xB5A750484C61746ELLU, // hnn_Latn_PH
+ 0xB9A7504B41726162LLU, // hno_Arab_PK
+ 0x686F50474C61746ELLU, // ho_Latn_PG
+ 0x89C7494E44657661LLU, // hoc_Deva_IN
+ 0xA5C7494E44657661LLU, // hoj_Deva_IN
+ 0x687248524C61746ELLU, // hr_Latn_HR
+ 0x864744454C61746ELLU, // hsb_Latn_DE
+ 0xB647434E48616E73LLU, // hsn_Hans_CN
+ 0x687448544C61746ELLU, // ht_Latn_HT
+ 0x687548554C61746ELLU, // hu_Latn_HU
+ 0x6879414D41726D6ELLU, // hy_Armn_AM
+ 0x687A4E414C61746ELLU, // hz_Latn_NA
+ 0x696146524C61746ELLU, // ia_Latn_FR
+ 0x80284D594C61746ELLU, // iba_Latn_MY
+ 0x84284E474C61746ELLU, // ibb_Latn_NG
+ 0x696449444C61746ELLU, // id_Latn_ID
+ 0x69674E474C61746ELLU, // ig_Latn_NG
+ 0x6969434E59696969LLU, // ii_Yiii_CN
+ 0x696B55534C61746ELLU, // ik_Latn_US
+ 0xCD4843414C61746ELLU, // ikt_Latn_CA
+ 0xB96850484C61746ELLU, // ilo_Latn_PH
+ 0x696E49444C61746ELLU, // in_Latn_ID
+ 0x9DA852554379726CLLU, // inh_Cyrl_RU
+ 0x697349534C61746ELLU, // is_Latn_IS
+ 0x697449544C61746ELLU, // it_Latn_IT
+ 0x6975434143616E73LLU, // iu_Cans_CA
+ 0x6977494C48656272LLU, // iw_Hebr_IL
+ 0x9F2852554C61746ELLU, // izh_Latn_RU
+ 0x6A614A504A70616ELLU, // ja_Jpan_JP
+ 0xB0094A4D4C61746ELLU, // jam_Latn_JM
+ 0xB8C9434D4C61746ELLU, // jgo_Latn_CM
+ 0x8989545A4C61746ELLU, // jmc_Latn_TZ
+ 0xAD894E5044657661LLU, // jml_Deva_NP
+ 0xCE89444B4C61746ELLU, // jut_Latn_DK
+ 0x6A7649444C61746ELLU, // jv_Latn_ID
+ 0x6A7749444C61746ELLU, // jw_Latn_ID
+ 0x6B61474547656F72LLU, // ka_Geor_GE
+ 0x800A555A4379726CLLU, // kaa_Cyrl_UZ
+ 0x840A445A4C61746ELLU, // kab_Latn_DZ
+ 0x880A4D4D4C61746ELLU, // kac_Latn_MM
+ 0xA40A4E474C61746ELLU, // kaj_Latn_NG
+ 0xB00A4B454C61746ELLU, // kam_Latn_KE
+ 0xB80A4D4C4C61746ELLU, // kao_Latn_ML
+ 0x8C2A52554379726CLLU, // kbd_Cyrl_RU
+ 0xE02A4E4541726162LLU, // kby_Arab_NE
+ 0x984A4E474C61746ELLU, // kcg_Latn_NG
+ 0xA84A5A574C61746ELLU, // kck_Latn_ZW
+ 0x906A545A4C61746ELLU, // kde_Latn_TZ
+ 0x9C6A544741726162LLU, // kdh_Arab_TG
+ 0xCC6A544854686169LLU, // kdt_Thai_TH
+ 0x808A43564C61746ELLU, // kea_Latn_CV
+ 0xB48A434D4C61746ELLU, // ken_Latn_CM
+ 0xB8AA43494C61746ELLU, // kfo_Latn_CI
+ 0xC4AA494E44657661LLU, // kfr_Deva_IN
+ 0xE0AA494E44657661LLU, // kfy_Deva_IN
+ 0x6B6743444C61746ELLU, // kg_Latn_CD
+ 0x90CA49444C61746ELLU, // kge_Latn_ID
+ 0xBCCA42524C61746ELLU, // kgp_Latn_BR
+ 0x80EA494E4C61746ELLU, // kha_Latn_IN
+ 0x84EA434E54616C75LLU, // khb_Talu_CN
+ 0xB4EA494E44657661LLU, // khn_Deva_IN
+ 0xC0EA4D4C4C61746ELLU, // khq_Latn_ML
+ 0xCCEA494E4D796D72LLU, // kht_Mymr_IN
+ 0xD8EA504B41726162LLU, // khw_Arab_PK
+ 0x6B694B454C61746ELLU, // ki_Latn_KE
+ 0xD10A54524C61746ELLU, // kiu_Latn_TR
+ 0x6B6A4E414C61746ELLU, // kj_Latn_NA
+ 0x992A4C414C616F6FLLU, // kjg_Laoo_LA
+ 0x6B6B434E41726162LLU, // kk_Arab_CN
+ 0x6B6B4B5A4379726CLLU, // kk_Cyrl_KZ
+ 0xA54A434D4C61746ELLU, // kkj_Latn_CM
+ 0x6B6C474C4C61746ELLU, // kl_Latn_GL
+ 0xB56A4B454C61746ELLU, // kln_Latn_KE
+ 0x6B6D4B484B686D72LLU, // km_Khmr_KH
+ 0x858A414F4C61746ELLU, // kmb_Latn_AO
+ 0x6B6E494E4B6E6461LLU, // kn_Knda_IN
+ 0x6B6F4B524B6F7265LLU, // ko_Kore_KR
+ 0xA1CA52554379726CLLU, // koi_Cyrl_RU
+ 0xA9CA494E44657661LLU, // kok_Deva_IN
+ 0xC9CA464D4C61746ELLU, // kos_Latn_FM
+ 0x91EA4C524C61746ELLU, // kpe_Latn_LR
+ 0x8A2A52554379726CLLU, // krc_Cyrl_RU
+ 0xA22A534C4C61746ELLU, // kri_Latn_SL
+ 0xA62A50484C61746ELLU, // krj_Latn_PH
+ 0xAE2A52554C61746ELLU, // krl_Latn_RU
+ 0xD22A494E44657661LLU, // kru_Deva_IN
+ 0x6B73494E41726162LLU, // ks_Arab_IN
+ 0x864A545A4C61746ELLU, // ksb_Latn_TZ
+ 0x964A434D4C61746ELLU, // ksf_Latn_CM
+ 0x9E4A44454C61746ELLU, // ksh_Latn_DE
+ 0x6B75495141726162LLU, // ku_Arab_IQ
+ 0x6B7554524C61746ELLU, // ku_Latn_TR
+ 0xB28A52554379726CLLU, // kum_Cyrl_RU
+ 0x6B7652554379726CLLU, // kv_Cyrl_RU
+ 0xC6AA49444C61746ELLU, // kvr_Latn_ID
+ 0xDEAA504B41726162LLU, // kvx_Arab_PK
+ 0x6B7747424C61746ELLU, // kw_Latn_GB
+ 0xB2EA544854686169LLU, // kxm_Thai_TH
+ 0xBEEA504B41726162LLU, // kxp_Arab_PK
+ 0x6B79434E41726162LLU, // ky_Arab_CN
+ 0x6B794B474379726CLLU, // ky_Cyrl_KG
+ 0x6B7954524C61746ELLU, // ky_Latn_TR
+ 0x6C6156414C61746ELLU, // la_Latn_VA
+ 0x840B47524C696E61LLU, // lab_Lina_GR
+ 0x8C0B494C48656272LLU, // lad_Hebr_IL
+ 0x980B545A4C61746ELLU, // lag_Latn_TZ
+ 0x9C0B504B41726162LLU, // lah_Arab_PK
+ 0xA40B55474C61746ELLU, // laj_Latn_UG
+ 0x6C624C554C61746ELLU, // lb_Latn_LU
+ 0x902B52554379726CLLU, // lbe_Cyrl_RU
+ 0xD82B49444C61746ELLU, // lbw_Latn_ID
+ 0xBC4B434E54686169LLU, // lcp_Thai_CN
+ 0xBC8B494E4C657063LLU, // lep_Lepc_IN
+ 0xE48B52554379726CLLU, // lez_Cyrl_RU
+ 0x6C6755474C61746ELLU, // lg_Latn_UG
+ 0x6C694E4C4C61746ELLU, // li_Latn_NL
+ 0x950B4E5044657661LLU, // lif_Deva_NP
+ 0x950B494E4C696D62LLU, // lif_Limb_IN
+ 0xA50B49544C61746ELLU, // lij_Latn_IT
+ 0xC90B434E4C697375LLU, // lis_Lisu_CN
+ 0xBD2B49444C61746ELLU, // ljp_Latn_ID
+ 0xA14B495241726162LLU, // lki_Arab_IR
+ 0xCD4B55534C61746ELLU, // lkt_Latn_US
+ 0xB58B494E54656C75LLU, // lmn_Telu_IN
+ 0xB98B49544C61746ELLU, // lmo_Latn_IT
+ 0x6C6E43444C61746ELLU, // ln_Latn_CD
+ 0x6C6F4C414C616F6FLLU, // lo_Laoo_LA
+ 0xADCB43444C61746ELLU, // lol_Latn_CD
+ 0xE5CB5A4D4C61746ELLU, // loz_Latn_ZM
+ 0x8A2B495241726162LLU, // lrc_Arab_IR
+ 0x6C744C544C61746ELLU, // lt_Latn_LT
+ 0x9A6B4C564C61746ELLU, // ltg_Latn_LV
+ 0x6C7543444C61746ELLU, // lu_Latn_CD
+ 0x828B43444C61746ELLU, // lua_Latn_CD
+ 0xBA8B4B454C61746ELLU, // luo_Latn_KE
+ 0xE28B4B454C61746ELLU, // luy_Latn_KE
+ 0xE68B495241726162LLU, // luz_Arab_IR
+ 0x6C764C564C61746ELLU, // lv_Latn_LV
+ 0xAECB544854686169LLU, // lwl_Thai_TH
+ 0x9F2B434E48616E73LLU, // lzh_Hans_CN
+ 0xE72B54524C61746ELLU, // lzz_Latn_TR
+ 0x8C0C49444C61746ELLU, // mad_Latn_ID
+ 0x940C434D4C61746ELLU, // maf_Latn_CM
+ 0x980C494E44657661LLU, // mag_Deva_IN
+ 0xA00C494E44657661LLU, // mai_Deva_IN
+ 0xA80C49444C61746ELLU, // mak_Latn_ID
+ 0xB40C474D4C61746ELLU, // man_Latn_GM
+ 0xB40C474E4E6B6F6FLLU, // man_Nkoo_GN
+ 0xC80C4B454C61746ELLU, // mas_Latn_KE
+ 0xE40C4D584C61746ELLU, // maz_Latn_MX
+ 0x946C52554379726CLLU, // mdf_Cyrl_RU
+ 0x9C6C50484C61746ELLU, // mdh_Latn_PH
+ 0xC46C49444C61746ELLU, // mdr_Latn_ID
+ 0xB48C534C4C61746ELLU, // men_Latn_SL
+ 0xC48C4B454C61746ELLU, // mer_Latn_KE
+ 0x80AC544841726162LLU, // mfa_Arab_TH
+ 0x90AC4D554C61746ELLU, // mfe_Latn_MU
+ 0x6D674D474C61746ELLU, // mg_Latn_MG
+ 0x9CCC4D5A4C61746ELLU, // mgh_Latn_MZ
+ 0xB8CC434D4C61746ELLU, // mgo_Latn_CM
+ 0xBCCC4E5044657661LLU, // mgp_Deva_NP
+ 0xE0CC545A4C61746ELLU, // mgy_Latn_TZ
+ 0x6D684D484C61746ELLU, // mh_Latn_MH
+ 0x6D694E5A4C61746ELLU, // mi_Latn_NZ
+ 0xB50C49444C61746ELLU, // min_Latn_ID
+ 0xC90C495148617472LLU, // mis_Hatr_IQ
+ 0x6D6B4D4B4379726CLLU, // mk_Cyrl_MK
+ 0x6D6C494E4D6C796DLLU, // ml_Mlym_IN
+ 0xC96C53444C61746ELLU, // mls_Latn_SD
+ 0x6D6E4D4E4379726CLLU, // mn_Cyrl_MN
+ 0x6D6E434E4D6F6E67LLU, // mn_Mong_CN
+ 0xA1AC494E42656E67LLU, // mni_Beng_IN
+ 0xD9AC4D4D4D796D72LLU, // mnw_Mymr_MM
+ 0x91CC43414C61746ELLU, // moe_Latn_CA
+ 0x9DCC43414C61746ELLU, // moh_Latn_CA
+ 0xC9CC42464C61746ELLU, // mos_Latn_BF
+ 0x6D72494E44657661LLU, // mr_Deva_IN
+ 0x8E2C4E5044657661LLU, // mrd_Deva_NP
+ 0xA62C52554379726CLLU, // mrj_Cyrl_RU
+ 0xBA2C42444D726F6FLLU, // mro_Mroo_BD
+ 0x6D734D594C61746ELLU, // ms_Latn_MY
+ 0x6D744D544C61746ELLU, // mt_Latn_MT
+ 0xC66C494E44657661LLU, // mtr_Deva_IN
+ 0x828C434D4C61746ELLU, // mua_Latn_CM
+ 0xCA8C55534C61746ELLU, // mus_Latn_US
+ 0xE2AC504B41726162LLU, // mvy_Arab_PK
+ 0xAACC4D4C4C61746ELLU, // mwk_Latn_ML
+ 0xC6CC494E44657661LLU, // mwr_Deva_IN
+ 0xD6CC49444C61746ELLU, // mwv_Latn_ID
+ 0x8AEC5A574C61746ELLU, // mxc_Latn_ZW
+ 0x6D794D4D4D796D72LLU, // my_Mymr_MM
+ 0xD70C52554379726CLLU, // myv_Cyrl_RU
+ 0xDF0C55474C61746ELLU, // myx_Latn_UG
+ 0xE70C49524D616E64LLU, // myz_Mand_IR
+ 0xB72C495241726162LLU, // mzn_Arab_IR
+ 0x6E614E524C61746ELLU, // na_Latn_NR
+ 0xB40D434E48616E73LLU, // nan_Hans_CN
+ 0xBC0D49544C61746ELLU, // nap_Latn_IT
+ 0xC00D4E414C61746ELLU, // naq_Latn_NA
+ 0x6E624E4F4C61746ELLU, // nb_Latn_NO
+ 0x9C4D4D584C61746ELLU, // nch_Latn_MX
+ 0x6E645A574C61746ELLU, // nd_Latn_ZW
+ 0x886D4D5A4C61746ELLU, // ndc_Latn_MZ
+ 0xC86D44454C61746ELLU, // nds_Latn_DE
+ 0x6E654E5044657661LLU, // ne_Deva_NP
+ 0xD88D4E5044657661LLU, // new_Deva_NP
+ 0x6E674E414C61746ELLU, // ng_Latn_NA
+ 0xACCD4D5A4C61746ELLU, // ngl_Latn_MZ
+ 0x90ED4D584C61746ELLU, // nhe_Latn_MX
+ 0xD8ED4D584C61746ELLU, // nhw_Latn_MX
+ 0xA50D49444C61746ELLU, // nij_Latn_ID
+ 0xD10D4E554C61746ELLU, // niu_Latn_NU
+ 0xB92D494E4C61746ELLU, // njo_Latn_IN
+ 0x6E6C4E4C4C61746ELLU, // nl_Latn_NL
+ 0x998D434D4C61746ELLU, // nmg_Latn_CM
+ 0x6E6E4E4F4C61746ELLU, // nn_Latn_NO
+ 0x9DAD434D4C61746ELLU, // nnh_Latn_CM
+ 0x6E6F4E4F4C61746ELLU, // no_Latn_NO
+ 0x8DCD54484C616E61LLU, // nod_Lana_TH
+ 0x91CD494E44657661LLU, // noe_Deva_IN
+ 0xB5CD534552756E72LLU, // non_Runr_SE
+ 0xBA0D474E4E6B6F6FLLU, // nqo_Nkoo_GN
+ 0x6E725A414C61746ELLU, // nr_Latn_ZA
+ 0xAA4D434143616E73LLU, // nsk_Cans_CA
+ 0xBA4D5A414C61746ELLU, // nso_Latn_ZA
+ 0xCA8D53534C61746ELLU, // nus_Latn_SS
+ 0x6E7655534C61746ELLU, // nv_Latn_US
+ 0xC2ED434E4C61746ELLU, // nxq_Latn_CN
+ 0x6E794D574C61746ELLU, // ny_Latn_MW
+ 0xB30D545A4C61746ELLU, // nym_Latn_TZ
+ 0xB70D55474C61746ELLU, // nyn_Latn_UG
+ 0xA32D47484C61746ELLU, // nzi_Latn_GH
+ 0x6F6346524C61746ELLU, // oc_Latn_FR
+ 0x6F6D45544C61746ELLU, // om_Latn_ET
+ 0x6F72494E4F727961LLU, // or_Orya_IN
+ 0x6F7347454379726CLLU, // os_Cyrl_GE
+ 0x824E55534F736765LLU, // osa_Osge_US
+ 0xAA6E4D4E4F726B68LLU, // otk_Orkh_MN
+ 0x7061504B41726162LLU, // pa_Arab_PK
+ 0x7061494E47757275LLU, // pa_Guru_IN
+ 0x980F50484C61746ELLU, // pag_Latn_PH
+ 0xAC0F495250686C69LLU, // pal_Phli_IR
+ 0xAC0F434E50686C70LLU, // pal_Phlp_CN
+ 0xB00F50484C61746ELLU, // pam_Latn_PH
+ 0xBC0F41574C61746ELLU, // pap_Latn_AW
+ 0xD00F50574C61746ELLU, // pau_Latn_PW
+ 0x8C4F46524C61746ELLU, // pcd_Latn_FR
+ 0xB04F4E474C61746ELLU, // pcm_Latn_NG
+ 0x886F55534C61746ELLU, // pdc_Latn_US
+ 0xCC6F43414C61746ELLU, // pdt_Latn_CA
+ 0xB88F49525870656FLLU, // peo_Xpeo_IR
+ 0xACAF44454C61746ELLU, // pfl_Latn_DE
+ 0xB4EF4C4250686E78LLU, // phn_Phnx_LB
+ 0x814F494E42726168LLU, // pka_Brah_IN
+ 0xB94F4B454C61746ELLU, // pko_Latn_KE
+ 0x706C504C4C61746ELLU, // pl_Latn_PL
+ 0xC98F49544C61746ELLU, // pms_Latn_IT
+ 0xCDAF47524772656BLLU, // pnt_Grek_GR
+ 0xB5CF464D4C61746ELLU, // pon_Latn_FM
+ 0x822F504B4B686172LLU, // pra_Khar_PK
+ 0x8E2F495241726162LLU, // prd_Arab_IR
+ 0x7073414641726162LLU, // ps_Arab_AF
+ 0x707442524C61746ELLU, // pt_Latn_BR
+ 0xD28F47414C61746ELLU, // puu_Latn_GA
+ 0x717550454C61746ELLU, // qu_Latn_PE
+ 0x8A9047544C61746ELLU, // quc_Latn_GT
+ 0x9A9045434C61746ELLU, // qug_Latn_EC
+ 0xA411494E44657661LLU, // raj_Deva_IN
+ 0x945152454C61746ELLU, // rcf_Latn_RE
+ 0xA49149444C61746ELLU, // rej_Latn_ID
+ 0xB4D149544C61746ELLU, // rgn_Latn_IT
+ 0x8111494E4C61746ELLU, // ria_Latn_IN
+ 0x95114D4154666E67LLU, // rif_Tfng_MA
+ 0xC9314E5044657661LLU, // rjs_Deva_NP
+ 0xCD51424442656E67LLU, // rkt_Beng_BD
+ 0x726D43484C61746ELLU, // rm_Latn_CH
+ 0x959146494C61746ELLU, // rmf_Latn_FI
+ 0xB99143484C61746ELLU, // rmo_Latn_CH
+ 0xCD91495241726162LLU, // rmt_Arab_IR
+ 0xD19153454C61746ELLU, // rmu_Latn_SE
+ 0x726E42494C61746ELLU, // rn_Latn_BI
+ 0x99B14D5A4C61746ELLU, // rng_Latn_MZ
+ 0x726F524F4C61746ELLU, // ro_Latn_RO
+ 0x85D149444C61746ELLU, // rob_Latn_ID
+ 0x95D1545A4C61746ELLU, // rof_Latn_TZ
+ 0xB271464A4C61746ELLU, // rtm_Latn_FJ
+ 0x727552554379726CLLU, // ru_Cyrl_RU
+ 0x929155414379726CLLU, // rue_Cyrl_UA
+ 0x9A9153424C61746ELLU, // rug_Latn_SB
+ 0x727752574C61746ELLU, // rw_Latn_RW
+ 0xAAD1545A4C61746ELLU, // rwk_Latn_TZ
+ 0xD3114A504B616E61LLU, // ryu_Kana_JP
+ 0x7361494E44657661LLU, // sa_Deva_IN
+ 0x941247484C61746ELLU, // saf_Latn_GH
+ 0x9C1252554379726CLLU, // sah_Cyrl_RU
+ 0xC0124B454C61746ELLU, // saq_Latn_KE
+ 0xC81249444C61746ELLU, // sas_Latn_ID
+ 0xCC12494E4C61746ELLU, // sat_Latn_IN
+ 0xE412494E53617572LLU, // saz_Saur_IN
+ 0xBC32545A4C61746ELLU, // sbp_Latn_TZ
+ 0x736349544C61746ELLU, // sc_Latn_IT
+ 0xA852494E44657661LLU, // sck_Deva_IN
+ 0xB45249544C61746ELLU, // scn_Latn_IT
+ 0xB85247424C61746ELLU, // sco_Latn_GB
+ 0xC85243414C61746ELLU, // scs_Latn_CA
+ 0x7364504B41726162LLU, // sd_Arab_PK
+ 0x7364494E44657661LLU, // sd_Deva_IN
+ 0x7364494E4B686F6ALLU, // sd_Khoj_IN
+ 0x7364494E53696E64LLU, // sd_Sind_IN
+ 0x887249544C61746ELLU, // sdc_Latn_IT
+ 0x9C72495241726162LLU, // sdh_Arab_IR
+ 0x73654E4F4C61746ELLU, // se_Latn_NO
+ 0x949243494C61746ELLU, // sef_Latn_CI
+ 0x9C924D5A4C61746ELLU, // seh_Latn_MZ
+ 0xA0924D584C61746ELLU, // sei_Latn_MX
+ 0xC8924D4C4C61746ELLU, // ses_Latn_ML
+ 0x736743464C61746ELLU, // sg_Latn_CF
+ 0x80D249454F67616DLLU, // sga_Ogam_IE
+ 0xC8D24C544C61746ELLU, // sgs_Latn_LT
+ 0xA0F24D4154666E67LLU, // shi_Tfng_MA
+ 0xB4F24D4D4D796D72LLU, // shn_Mymr_MM
+ 0x73694C4B53696E68LLU, // si_Sinh_LK
+ 0x8D1245544C61746ELLU, // sid_Latn_ET
+ 0x736B534B4C61746ELLU, // sk_Latn_SK
+ 0xC552504B41726162LLU, // skr_Arab_PK
+ 0x736C53494C61746ELLU, // sl_Latn_SI
+ 0xA172504C4C61746ELLU, // sli_Latn_PL
+ 0xE17249444C61746ELLU, // sly_Latn_ID
+ 0x736D57534C61746ELLU, // sm_Latn_WS
+ 0x819253454C61746ELLU, // sma_Latn_SE
+ 0xA59253454C61746ELLU, // smj_Latn_SE
+ 0xB59246494C61746ELLU, // smn_Latn_FI
+ 0xBD92494C53616D72LLU, // smp_Samr_IL
+ 0xC99246494C61746ELLU, // sms_Latn_FI
+ 0x736E5A574C61746ELLU, // sn_Latn_ZW
+ 0xA9B24D4C4C61746ELLU, // snk_Latn_ML
+ 0x736F534F4C61746ELLU, // so_Latn_SO
+ 0xD1D2544854686169LLU, // sou_Thai_TH
+ 0x7371414C4C61746ELLU, // sq_Latn_AL
+ 0x737252534379726CLLU, // sr_Cyrl_RS
+ 0x737252534C61746ELLU, // sr_Latn_RS
+ 0x8632494E536F7261LLU, // srb_Sora_IN
+ 0xB63253524C61746ELLU, // srn_Latn_SR
+ 0xC632534E4C61746ELLU, // srr_Latn_SN
+ 0xDE32494E44657661LLU, // srx_Deva_IN
+ 0x73735A414C61746ELLU, // ss_Latn_ZA
+ 0xE25245524C61746ELLU, // ssy_Latn_ER
+ 0x73745A414C61746ELLU, // st_Latn_ZA
+ 0xC27244454C61746ELLU, // stq_Latn_DE
+ 0x737549444C61746ELLU, // su_Latn_ID
+ 0xAA92545A4C61746ELLU, // suk_Latn_TZ
+ 0xCA92474E4C61746ELLU, // sus_Latn_GN
+ 0x737653454C61746ELLU, // sv_Latn_SE
+ 0x7377545A4C61746ELLU, // sw_Latn_TZ
+ 0x86D2595441726162LLU, // swb_Arab_YT
+ 0x8AD243444C61746ELLU, // swc_Latn_CD
+ 0x9AD244454C61746ELLU, // swg_Latn_DE
+ 0xD6D2494E44657661LLU, // swv_Deva_IN
+ 0xB6F249444C61746ELLU, // sxn_Latn_ID
+ 0xAF12424442656E67LLU, // syl_Beng_BD
+ 0xC712495153797263LLU, // syr_Syrc_IQ
+ 0xAF32504C4C61746ELLU, // szl_Latn_PL
+ 0x7461494E54616D6CLLU, // ta_Taml_IN
+ 0xA4134E5044657661LLU, // taj_Deva_NP
+ 0xD83350484C61746ELLU, // tbw_Latn_PH
+ 0xE053494E4B6E6461LLU, // tcy_Knda_IN
+ 0x8C73434E54616C65LLU, // tdd_Tale_CN
+ 0x98734E5044657661LLU, // tdg_Deva_NP
+ 0x9C734E5044657661LLU, // tdh_Deva_NP
+ 0x7465494E54656C75LLU, // te_Telu_IN
+ 0xB093534C4C61746ELLU, // tem_Latn_SL
+ 0xB89355474C61746ELLU, // teo_Latn_UG
+ 0xCC93544C4C61746ELLU, // tet_Latn_TL
+ 0x7467504B41726162LLU, // tg_Arab_PK
+ 0x7467544A4379726CLLU, // tg_Cyrl_TJ
+ 0x7468544854686169LLU, // th_Thai_TH
+ 0xACF34E5044657661LLU, // thl_Deva_NP
+ 0xC0F34E5044657661LLU, // thq_Deva_NP
+ 0xC4F34E5044657661LLU, // thr_Deva_NP
+ 0x7469455445746869LLU, // ti_Ethi_ET
+ 0x9913455245746869LLU, // tig_Ethi_ER
+ 0xD5134E474C61746ELLU, // tiv_Latn_NG
+ 0x746B544D4C61746ELLU, // tk_Latn_TM
+ 0xAD53544B4C61746ELLU, // tkl_Latn_TK
+ 0xC553415A4C61746ELLU, // tkr_Latn_AZ
+ 0xCD534E5044657661LLU, // tkt_Deva_NP
+ 0x746C50484C61746ELLU, // tl_Latn_PH
+ 0xE173415A4C61746ELLU, // tly_Latn_AZ
+ 0x9D934E454C61746ELLU, // tmh_Latn_NE
+ 0x746E5A414C61746ELLU, // tn_Latn_ZA
+ 0x746F544F4C61746ELLU, // to_Latn_TO
+ 0x99D34D574C61746ELLU, // tog_Latn_MW
+ 0xA1F350474C61746ELLU, // tpi_Latn_PG
+ 0x747254524C61746ELLU, // tr_Latn_TR
+ 0xD23354524C61746ELLU, // tru_Latn_TR
+ 0xD63354574C61746ELLU, // trv_Latn_TW
+ 0x74735A414C61746ELLU, // ts_Latn_ZA
+ 0x8E5347524772656BLLU, // tsd_Grek_GR
+ 0x96534E5044657661LLU, // tsf_Deva_NP
+ 0x9A5350484C61746ELLU, // tsg_Latn_PH
+ 0xA653425454696274LLU, // tsj_Tibt_BT
+ 0x747452554379726CLLU, // tt_Cyrl_RU
+ 0xA67355474C61746ELLU, // ttj_Latn_UG
+ 0xCA73544854686169LLU, // tts_Thai_TH
+ 0xCE73415A4C61746ELLU, // ttt_Latn_AZ
+ 0xB2934D574C61746ELLU, // tum_Latn_MW
+ 0xAEB354564C61746ELLU, // tvl_Latn_TV
+ 0xC2D34E454C61746ELLU, // twq_Latn_NE
+ 0x9AF3434E54616E67LLU, // txg_Tang_CN
+ 0x747950464C61746ELLU, // ty_Latn_PF
+ 0xD71352554379726CLLU, // tyv_Cyrl_RU
+ 0xB3334D414C61746ELLU, // tzm_Latn_MA
+ 0xB07452554379726CLLU, // udm_Cyrl_RU
+ 0x7567434E41726162LLU, // ug_Arab_CN
+ 0x75674B5A4379726CLLU, // ug_Cyrl_KZ
+ 0x80D4535955676172LLU, // uga_Ugar_SY
+ 0x756B55414379726CLLU, // uk_Cyrl_UA
+ 0xA174464D4C61746ELLU, // uli_Latn_FM
+ 0x8594414F4C61746ELLU, // umb_Latn_AO
+ 0xC5B4494E42656E67LLU, // unr_Beng_IN
+ 0xC5B44E5044657661LLU, // unr_Deva_NP
+ 0xDDB4494E42656E67LLU, // unx_Beng_IN
+ 0x7572504B41726162LLU, // ur_Arab_PK
+ 0x757A414641726162LLU, // uz_Arab_AF
+ 0x757A555A4C61746ELLU, // uz_Latn_UZ
+ 0xA0154C5256616969LLU, // vai_Vaii_LR
+ 0x76655A414C61746ELLU, // ve_Latn_ZA
+ 0x889549544C61746ELLU, // vec_Latn_IT
+ 0xBC9552554C61746ELLU, // vep_Latn_RU
+ 0x7669564E4C61746ELLU, // vi_Latn_VN
+ 0x891553584C61746ELLU, // vic_Latn_SX
+ 0xC97542454C61746ELLU, // vls_Latn_BE
+ 0x959544454C61746ELLU, // vmf_Latn_DE
+ 0xD9954D5A4C61746ELLU, // vmw_Latn_MZ
+ 0xCDD552554C61746ELLU, // vot_Latn_RU
+ 0xBA3545454C61746ELLU, // vro_Latn_EE
+ 0xB695545A4C61746ELLU, // vun_Latn_TZ
+ 0x776142454C61746ELLU, // wa_Latn_BE
+ 0x901643484C61746ELLU, // wae_Latn_CH
+ 0xAC16455445746869LLU, // wal_Ethi_ET
+ 0xC41650484C61746ELLU, // war_Latn_PH
+ 0xBC3641554C61746ELLU, // wbp_Latn_AU
+ 0xC036494E54656C75LLU, // wbq_Telu_IN
+ 0xC436494E44657661LLU, // wbr_Deva_IN
+ 0xC97657464C61746ELLU, // wls_Latn_WF
+ 0xA1B64B4D41726162LLU, // wni_Arab_KM
+ 0x776F534E4C61746ELLU, // wo_Latn_SN
+ 0xB276494E44657661LLU, // wtm_Deva_IN
+ 0xD296434E48616E73LLU, // wuu_Hans_CN
+ 0xD41742524C61746ELLU, // xav_Latn_BR
+ 0xC457545243617269LLU, // xcr_Cari_TR
+ 0x78685A414C61746ELLU, // xh_Latn_ZA
+ 0x897754524C796369LLU, // xlc_Lyci_TR
+ 0x8D7754524C796469LLU, // xld_Lydi_TR
+ 0x9597474547656F72LLU, // xmf_Geor_GE
+ 0xB597434E4D616E69LLU, // xmn_Mani_CN
+ 0xC59753444D657263LLU, // xmr_Merc_SD
+ 0x81B753414E617262LLU, // xna_Narb_SA
+ 0xC5B7494E44657661LLU, // xnr_Deva_IN
+ 0x99D755474C61746ELLU, // xog_Latn_UG
+ 0xC5F7495250727469LLU, // xpr_Prti_IR
+ 0x8257594553617262LLU, // xsa_Sarb_YE
+ 0xC6574E5044657661LLU, // xsr_Deva_NP
+ 0xB8184D5A4C61746ELLU, // yao_Latn_MZ
+ 0xBC18464D4C61746ELLU, // yap_Latn_FM
+ 0xD418434D4C61746ELLU, // yav_Latn_CM
+ 0x8438434D4C61746ELLU, // ybb_Latn_CM
+ 0x796F4E474C61746ELLU, // yo_Latn_NG
+ 0xAE3842524C61746ELLU, // yrl_Latn_BR
+ 0x82984D584C61746ELLU, // yua_Latn_MX
+ 0x9298434E48616E73LLU, // yue_Hans_CN
+ 0x9298484B48616E74LLU, // yue_Hant_HK
+ 0x7A61434E4C61746ELLU, // za_Latn_CN
+ 0x981953444C61746ELLU, // zag_Latn_SD
+ 0xA4794B4D41726162LLU, // zdj_Arab_KM
+ 0x80994E4C4C61746ELLU, // zea_Latn_NL
+ 0x9CD94D4154666E67LLU, // zgh_Tfng_MA
+ 0x7A685457426F706FLLU, // zh_Bopo_TW
+ 0x7A68545748616E62LLU, // zh_Hanb_TW
+ 0x7A68434E48616E73LLU, // zh_Hans_CN
+ 0x7A68545748616E74LLU, // zh_Hant_TW
+ 0xB17954474C61746ELLU, // zlm_Latn_TG
+ 0xA1994D594C61746ELLU, // zmi_Latn_MY
+ 0x7A755A414C61746ELLU, // zu_Latn_ZA
+ 0x833954524C61746ELLU, // zza_Latn_TR
});
const std::unordered_map<uint32_t, uint32_t> ARAB_PARENTS({
diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS
index 23ec5ab..f1903a5 100644
--- a/libs/androidfw/OWNERS
+++ b/libs/androidfw/OWNERS
@@ -1,2 +1,3 @@
set noparent
toddke@google.com
+rtmitchell@google.com
\ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 91261aa..cf2d8fb 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1622,7 +1622,7 @@
{
struct ResChunk_header header;
- enum PolicyFlags {
+ enum PolicyFlags : uint32_t {
// Any overlay can overlay these resources.
POLICY_PUBLIC = 0x00000001,
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index a2e8672..89d3cc4 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -14,7 +14,6 @@
cc_library_shared {
name: "libinputservice",
- cpp_std: "c++17",
srcs: [
"PointerController.cpp",
"SpriteController.cpp",
diff --git a/media/Android.bp b/media/Android.bp
new file mode 100644
index 0000000..d5da6f2
--- /dev/null
+++ b/media/Android.bp
@@ -0,0 +1,36 @@
+java_library {
+ // TODO: include media2.jar in the media apex and add it to the bootclasspath.
+ name: "media2",
+
+ srcs: [
+ ":media2-srcs",
+ ":framework-media-annotation-srcs",
+ ],
+
+ static_libs: [
+ "mediaplayer2-protos",
+ ],
+
+ // Make sure that the implementaion only relies on SDK or system APIs.
+ sdk_version: "system_current",
+}
+
+filegroup {
+ name: "media2-srcs",
+ srcs: [
+ "java/android/media/CloseGuard.java",
+ "java/android/media/DataSourceCallback.java",
+ "java/android/media/DataSourceDesc.java",
+ "java/android/media/UriDataSourceDesc.java",
+ "java/android/media/FileDataSourceDesc.java",
+ "java/android/media/CallbackDataSourceDesc.java",
+ "java/android/media/VideoSize.java",
+ "java/android/media/Media2Utils.java",
+ "java/android/media/MediaPlayer2Utils.java",
+ "java/android/media/MediaPlayer2.java",
+ "java/android/media/Media2HTTPService.java",
+ "java/android/media/Media2HTTPConnection.java",
+ "java/android/media/RoutingDelegate.java",
+ "java/android/media/BufferingParams.java",
+ ],
+}
diff --git a/media/java/android/media/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java
index 5467a69..0a9ca02 100644
--- a/media/java/android/media/AudioFocusInfo.java
+++ b/media/java/android/media/AudioFocusInfo.java
@@ -80,16 +80,12 @@
* The audio attributes for the audio focus request.
* @return non-null {@link AudioAttributes}.
*/
- @SystemApi
public AudioAttributes getAttributes() { return mAttributes; }
- @SystemApi
public int getClientUid() { return mClientUid; }
- @SystemApi
public String getClientId() { return mClientId; }
- @SystemApi
public String getPackageName() { return mPackageName; }
/**
@@ -99,7 +95,6 @@
* {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK},
* {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}.
*/
- @SystemApi
public int getGainRequest() { return mGainRequest; }
/**
@@ -109,7 +104,6 @@
* {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT} or
* {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
*/
- @SystemApi
public int getLossReceived() { return mLossReceived; }
/** @hide */
@@ -124,7 +118,6 @@
* {@link AudioManager#AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}, and
* {@link AudioManager#AUDIOFOCUS_FLAG_LOCK}.
*/
- @SystemApi
public int getFlags() { return mFlags; }
@Override
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d10900e..da52a26 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5152,6 +5152,15 @@
return reportedSurroundFormats;
}
+ /**
+ * Return if audio haptic coupled playback is supported or not.
+ *
+ * @return whether audio haptic playback supported.
+ */
+ public static boolean isHapticPlaybackSupported() {
+ return AudioSystem.isHapticPlaybackSupported();
+ }
+
//---------------------------------------------------------
// Inner classes
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index 823af65..fca7074 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -254,6 +254,22 @@
mLabels.hashCode());
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getSimpleName() + " ");
+ sb.append("{ presentation id=" + mPresentationId);
+ sb.append(", program id=" + mProgramId);
+ sb.append(", language=" + mLanguage);
+ sb.append(", labels=" + mLabels);
+ sb.append(", mastering indication=" + mMasteringIndication);
+ sb.append(", audio description=" + mAudioDescriptionAvailable);
+ sb.append(", spoken subtitles=" + mSpokenSubtitlesAvailable);
+ sb.append(", dialogue enhancement=" + mDialogueEnhancementAvailable);
+ sb.append(" }");
+ return sb.toString();
+ }
+
/**
* A builder class for creating {@link AudioPresentation} objects.
*/
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 082a375..36f635a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -933,6 +933,11 @@
*/
public static native int setA11yServicesUids(int[] uids);
+ /**
+ * @see AudioManager#isHapticPlaybackSupported()
+ */
+ public static native boolean isHapticPlaybackSupported();
+
// Items shared with audio service
/**
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 242ae46..0375de3 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -27,7 +27,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.IHwBinder;
import android.os.Looper;
import android.os.Message;
@@ -1427,6 +1426,24 @@
<td>⎆</td>
</tr>
<tr>
+ <td>(29+)</td>
+ <td>29+</td>
+ <td>29+</td>
+ <td>29+</td>
+ <td>(29+)</td>
+ <td>(29+)</td>
+ <td>-</td>
+ <td class=fn>{@link #setAudioPresentation setAudioPresentation}</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
<td>-</td>
<td>-</td>
<td>18+</td>
@@ -3260,6 +3277,19 @@
public native final void setVideoScalingMode(@VideoScalingMode int mode);
/**
+ * Sets the audio presentation.
+ * @param presentation see {@link AudioPresentation}. In particular, id should be set.
+ */
+ public void setAudioPresentation(@NonNull AudioPresentation presentation) {
+ if (presentation == null) {
+ throw new IllegalArgumentException("audio presentation is null");
+ }
+ native_setAudioPresentation(presentation.getPresentationId(), presentation.getProgramId());
+ }
+
+ private native void native_setAudioPresentation(int presentationId, int programId);
+
+ /**
* Get the component name. If the codec was created by createDecoderByType
* or createEncoderByType, what component is chosen is not known beforehand.
* @throws IllegalStateException if in the Released state.
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index c91d4d3..0fb392b 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -18,10 +18,10 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
+
import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
@@ -269,8 +269,10 @@
public static final int MUXER_OUTPUT_3GPP = MUXER_OUTPUT_FIRST + 2;
/** HEIF media file format*/
public static final int MUXER_OUTPUT_HEIF = MUXER_OUTPUT_FIRST + 3;
+ /** Ogg media file format*/
+ public static final int MUXER_OUTPUT_OGG = MUXER_OUTPUT_FIRST + 4;
/** @hide */
- public static final int MUXER_OUTPUT_LAST = MUXER_OUTPUT_HEIF;
+ public static final int MUXER_OUTPUT_LAST = MUXER_OUTPUT_OGG;
};
/** @hide */
@@ -279,6 +281,7 @@
OutputFormat.MUXER_OUTPUT_WEBM,
OutputFormat.MUXER_OUTPUT_3GPP,
OutputFormat.MUXER_OUTPUT_HEIF,
+ OutputFormat.MUXER_OUTPUT_OGG,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Format {}
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 9038f72..d4b1c7f 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -3960,7 +3960,12 @@
textBounds = new Rect(left, top, right, bottom);
}
}
+ return null;
+ /* TimedText c-tor usage is temporarily commented out.
+ * TODO(b/117527789): use SUBTITLE path for MEDIA_MIMETYPE_TEXT_3GPP track
+ * and remove TimedText path from MediaPlayer2.
return new TimedText(textChars, textBounds);
+ */
}
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index d4bfd61..8ced021 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -22,17 +22,17 @@
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.hardware.Camera;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.view.Surface;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -41,8 +41,6 @@
import java.util.ArrayList;
import java.util.List;
-import com.android.internal.annotations.GuardedBy;
-
/**
* Used to record audio and video. The recording control is based on a
* simple state machine (see below).
@@ -450,6 +448,9 @@
/** VP8/VORBIS data in a WEBM container */
public static final int WEBM = 9;
+
+ /** Opus data in a Ogg container */
+ public static final int OGG = 11;
};
/**
@@ -474,6 +475,8 @@
public static final int AAC_ELD = 5;
/** Ogg Vorbis audio codec */
public static final int VORBIS = 6;
+ /** Opus audio codec */
+ public static final int OPUS = 7;
}
/**
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 8bc1d35..7fb3aa6 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -81,14 +81,12 @@
* An audio mix behavior where the output of the mix is sent to the original destination of
* the audio signal, i.e. an output device for an output mix, or a recording for an input mix.
*/
- @SystemApi
public static final int ROUTE_FLAG_RENDER = 0x1;
/**
* An audio mix behavior where the output of the mix is rerouted back to the framework and
* is accessible for injection or capture through the {@link AudioTrack} and {@link AudioRecord}
* APIs.
*/
- @SystemApi
public static final int ROUTE_FLAG_LOOP_BACK = 0x1 << 1;
private static final int ROUTE_FLAG_SUPPORTED = ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK;
@@ -113,31 +111,23 @@
// MIX_STATE_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h
/**
- * @hide
* State of a mix before its policy is enabled.
*/
- @SystemApi
public static final int MIX_STATE_DISABLED = -1;
/**
- * @hide
* State of a mix when there is no audio to mix.
*/
- @SystemApi
public static final int MIX_STATE_IDLE = 0;
/**
- * @hide
* State of a mix that is actively mixing audio.
*/
- @SystemApi
public static final int MIX_STATE_MIXING = 1;
/**
- * @hide
* The current mixing state.
* @return one of {@link #MIX_STATE_DISABLED}, {@link #MIX_STATE_IDLE},
* {@link #MIX_STATE_MIXING}.
*/
- @SystemApi
public int getMixState() {
return mMixState;
}
@@ -201,9 +191,7 @@
/**
* Builder class for {@link AudioMix} objects
- *
*/
- @SystemApi
public static class Builder {
private AudioMixingRule mRule = null;
private AudioFormat mFormat = null;
@@ -224,7 +212,6 @@
* @param rule a non-null {@link AudioMixingRule} instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder(AudioMixingRule rule)
throws IllegalArgumentException {
if (rule == null) {
@@ -284,7 +271,6 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder setFormat(AudioFormat format)
throws IllegalArgumentException {
if (format == null) {
@@ -302,7 +288,6 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder setRouteFlags(@RouteFlags int routeFlags)
throws IllegalArgumentException {
if (routeFlags == 0) {
@@ -329,7 +314,6 @@
* @return the same Builder instance
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder setDevice(@NonNull AudioDeviceInfo device) throws IllegalArgumentException {
if (device == null) {
throw new IllegalArgumentException("Illegal null AudioDeviceInfo argument");
@@ -347,7 +331,6 @@
* @return a new {@link AudioMix} object
* @throws IllegalArgumentException if no {@link AudioMixingRule} has been set.
*/
- @SystemApi
public AudioMix build() throws IllegalArgumentException {
if (mRule == null) {
throw new IllegalArgumentException("Illegal null AudioMixingRule");
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index fbee62a..6c48cdb 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -54,7 +54,6 @@
* {@link Builder#addMixRule(int, Object)} where the Object parameter is an instance of
* {@link AudioAttributes}.
*/
- @SystemApi
public static final int RULE_MATCH_ATTRIBUTE_USAGE = 0x1;
/**
* A rule requiring the capture preset information of the {@link AudioAttributes} to match.
@@ -62,14 +61,12 @@
* {@link Builder#addMixRule(int, Object)} where the Object parameter is an instance of
* {@link AudioAttributes}.
*/
- @SystemApi
public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 0x1 << 1;
/**
* A rule requiring the UID of the audio stream to match that specified.
* This mixing rule can be added with {@link Builder#addMixRule(int, Object)} where the Object
* parameter is an instance of {@link java.lang.Integer}.
*/
- @SystemApi
public static final int RULE_MATCH_UID = 0x1 << 2;
private final static int RULE_EXCLUSION_MASK = 0x8000;
@@ -239,7 +236,6 @@
/**
* Builder class for {@link AudioMixingRule} objects
*/
- @SystemApi
public static class Builder {
private ArrayList<AudioMixMatchCriterion> mCriteria;
private int mTargetMixType = AudioMix.MIX_TYPE_INVALID;
@@ -247,7 +243,6 @@
/**
* Constructs a new Builder with no rules.
*/
- @SystemApi
public Builder() {
mCriteria = new ArrayList<AudioMixMatchCriterion>();
}
@@ -262,7 +257,6 @@
* @throws IllegalArgumentException
* @see #excludeRule(AudioAttributes, int)
*/
- @SystemApi
public Builder addRule(AudioAttributes attrToMatch, int rule)
throws IllegalArgumentException {
if (!isValidAttributesSystemApiRule(rule)) {
@@ -291,7 +285,6 @@
* @throws IllegalArgumentException
* @see #addRule(AudioAttributes, int)
*/
- @SystemApi
public Builder excludeRule(AudioAttributes attrToMatch, int rule)
throws IllegalArgumentException {
if (!isValidAttributesSystemApiRule(rule)) {
@@ -313,7 +306,6 @@
* @throws IllegalArgumentException
* @see #excludeMixRule(int, Object)
*/
- @SystemApi
public Builder addMixRule(int rule, Object property) throws IllegalArgumentException {
if (!isValidSystemApiRule(rule)) {
throw new IllegalArgumentException("Illegal rule value " + rule);
@@ -343,7 +335,6 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder excludeMixRule(int rule, Object property) throws IllegalArgumentException {
if (!isValidSystemApiRule(rule)) {
throw new IllegalArgumentException("Illegal rule value " + rule);
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 11107e2..6103f557 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -58,12 +58,10 @@
/**
* The status of an audio policy that is valid but cannot be used because it is not registered.
*/
- @SystemApi
public static final int POLICY_STATUS_UNREGISTERED = 1;
/**
* The status of an audio policy that is valid, successfully registered and thus active.
*/
- @SystemApi
public static final int POLICY_STATUS_REGISTERED = 2;
private int mStatus;
@@ -75,7 +73,6 @@
* The behavior of a policy with regards to audio focus where it relies on the application
* to do the ducking, the is the legacy and default behavior.
*/
- @SystemApi
public static final int FOCUS_POLICY_DUCKING_IN_APP = 0;
public static final int FOCUS_POLICY_DUCKING_DEFAULT = FOCUS_POLICY_DUCKING_IN_APP;
/**
@@ -85,7 +82,6 @@
* <br>Can only be used after having set a listener with
* {@link AudioPolicy#setAudioPolicyFocusListener(AudioPolicyFocusListener)}.
*/
- @SystemApi
public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1;
private AudioPolicyFocusListener mFocusListener;
@@ -133,7 +129,6 @@
* Builder class for {@link AudioPolicy} objects.
* By default the policy to be created doesn't govern audio focus decisions.
*/
- @SystemApi
public static class Builder {
private ArrayList<AudioMix> mMixes;
private Context mContext;
@@ -147,7 +142,6 @@
* Constructs a new Builder with no audio mixes.
* @param context the context for the policy
*/
- @SystemApi
public Builder(Context context) {
mMixes = new ArrayList<AudioMix>();
mContext = context;
@@ -159,7 +153,6 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder addMix(@NonNull AudioMix mix) throws IllegalArgumentException {
if (mix == null) {
throw new IllegalArgumentException("Illegal null AudioMix argument");
@@ -174,7 +167,6 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- @SystemApi
public Builder setLooper(@NonNull Looper looper) throws IllegalArgumentException {
if (looper == null) {
throw new IllegalArgumentException("Illegal null Looper argument");
@@ -187,7 +179,6 @@
* Sets the audio focus listener for the policy.
* @param l a {@link AudioPolicy.AudioPolicyFocusListener}
*/
- @SystemApi
public void setAudioPolicyFocusListener(AudioPolicyFocusListener l) {
mFocusListener = l;
}
@@ -201,7 +192,6 @@
* @param enforce true if the policy will govern audio focus decisions.
* @return the same Builder instance.
*/
- @SystemApi
public Builder setIsAudioFocusPolicy(boolean isFocusPolicy) {
mIsFocusPolicy = isFocusPolicy;
return this;
@@ -211,12 +201,10 @@
* Sets the audio policy status listener.
* @param l a {@link AudioPolicy.AudioPolicyStatusListener}
*/
- @SystemApi
public void setAudioPolicyStatusListener(AudioPolicyStatusListener l) {
mStatusListener = l;
}
- @SystemApi
/**
* Sets the callback to receive all volume key-related events.
* The callback will only be called if the device is configured to handle volume events
@@ -240,7 +228,6 @@
* {@link AudioPolicy.AudioPolicyStatusListener} but the policy was configured
* as an audio focus policy with {@link #setIsAudioFocusPolicy(boolean)}.
*/
- @SystemApi
public AudioPolicy build() {
if (mStatusListener != null) {
// the AudioPolicy status listener includes updates on each mix activity state
@@ -258,7 +245,6 @@
}
/**
- * @hide
* Update the current configuration of the set of audio mixes by adding new ones, while
* keeping the policy registered.
* This method can only be called on a registered policy.
@@ -266,7 +252,6 @@
* @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
* otherwise.
*/
- @SystemApi
public int attachMixes(@NonNull List<AudioMix> mixes) {
if (mixes == null) {
throw new IllegalArgumentException("Illegal null list of AudioMix");
@@ -299,7 +284,6 @@
}
/**
- * @hide
* Update the current configuration of the set of audio mixes by removing some, while
* keeping the policy registered.
* This method can only be called on a registered policy.
@@ -307,7 +291,6 @@
* @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
* otherwise.
*/
- @SystemApi
public int detachMixes(@NonNull List<AudioMix> mixes) {
if (mixes == null) {
throw new IllegalArgumentException("Illegal null list of AudioMix");
@@ -405,7 +388,6 @@
* Returns the current behavior for audio focus-related ducking.
* @return {@link #FOCUS_POLICY_DUCKING_IN_APP} or {@link #FOCUS_POLICY_DUCKING_IN_POLICY}
*/
- @SystemApi
public int getFocusDuckingBehavior() {
return mConfig.mDuckingPolicy;
}
@@ -422,7 +404,6 @@
* @throws IllegalArgumentException
* @throws IllegalStateException
*/
- @SystemApi
public int setFocusDuckingBehavior(int behavior)
throws IllegalArgumentException, IllegalStateException {
if ((behavior != FOCUS_POLICY_DUCKING_IN_APP)
@@ -466,7 +447,6 @@
* with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
* @throws IllegalArgumentException
*/
- @SystemApi
public AudioRecord createAudioRecordSink(AudioMix mix) throws IllegalArgumentException {
if (!policyReadyToUse()) {
Log.e(TAG, "Cannot create AudioRecord sink for AudioMix");
@@ -506,7 +486,6 @@
* with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
* @throws IllegalArgumentException
*/
- @SystemApi
public AudioTrack createAudioTrackSource(AudioMix mix) throws IllegalArgumentException {
if (!policyReadyToUse()) {
Log.e(TAG, "Cannot create AudioTrack source for AudioMix");
@@ -528,18 +507,15 @@
return at;
}
- @SystemApi
public int getStatus() {
return mStatus;
}
- @SystemApi
public static abstract class AudioPolicyStatusListener {
public void onStatusChange() {}
public void onMixStateUpdate(AudioMix mix) {}
}
- @SystemApi
public static abstract class AudioPolicyFocusListener {
public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {}
public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {}
@@ -563,7 +539,6 @@
public void onAudioFocusAbandon(AudioFocusInfo afi) {}
}
- @SystemApi
/**
* Callback class to receive volume change-related events.
* See {@link #Builder.setAudioPolicyVolumeCallback(AudioPolicyCallback)} to configure the
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index ec2d9ba..e8f188c 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1332,7 +1332,6 @@
*
* @return the list of content ratings blocked by the user.
*/
- @SystemApi
public List<TvContentRating> getBlockedRatings() {
try {
List<TvContentRating> ratings = new ArrayList<>();
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 5037209..2578608 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -752,6 +752,13 @@
}
}
+void JMediaCodec::selectAudioPresentation(const int32_t presentationId, const int32_t programId) {
+ sp<AMessage> msg = new AMessage;
+ msg->setInt32("audio-presentation-presentation-id", presentationId);
+ msg->setInt32("audio-presentation-program-id", programId);
+ (void)mCodec->setParameters(msg);
+}
+
static jthrowable createCodecException(
JNIEnv *env, status_t err, int32_t actionCode, const char *msg = NULL) {
ScopedLocalRef<jclass> clazz(
@@ -1874,6 +1881,18 @@
codec->setVideoScalingMode(mode);
}
+static void android_media_MediaCodec_setAudioPresentation(
+ JNIEnv *env, jobject thiz, jint presentationId, jint programId) {
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ codec->selectAudioPresentation((int32_t)presentationId, (int32_t)programId);
+}
+
static void android_media_MediaCodec_native_init(JNIEnv *env) {
ScopedLocalRef<jclass> clazz(
env, env->FindClass("android/media/MediaCodec"));
@@ -2183,6 +2202,9 @@
{ "setVideoScalingMode", "(I)V",
(void *)android_media_MediaCodec_setVideoScalingMode },
+ { "native_setAudioPresentation", "(II)V",
+ (void *)android_media_MediaCodec_setAudioPresentation },
+
{ "native_init", "()V", (void *)android_media_MediaCodec_native_init },
{ "native_setup", "(Ljava/lang/String;ZZ)V",
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 985f55a..0a53f1a 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -128,6 +128,8 @@
void setVideoScalingMode(int mode);
+ void selectAudioPresentation(const int32_t presentationId, const int32_t programId);
+
protected:
virtual ~JMediaCodec();
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 29238d3..f3442f4 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -500,17 +500,17 @@
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return -1ll;
+ return -1LL;
}
int64_t sampleTimeUs;
status_t err = extractor->getSampleTime(&sampleTimeUs);
if (err == ERROR_END_OF_STREAM) {
- return -1ll;
+ return -1LL;
} else if (err != OK) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return -1ll;
+ return -1LL;
}
return (jlong) sampleTimeUs;
@@ -522,17 +522,17 @@
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return -1ll;
+ return -1LL;
}
size_t sampleSize;
status_t err = extractor->getSampleSize(&sampleSize);
if (err == ERROR_END_OF_STREAM) {
- return -1ll;
+ return -1LL;
} else if (err != OK) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return -1ll;
+ return -1LL;
}
return (jlong) sampleSize;
@@ -858,13 +858,13 @@
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return -1ll;
+ return -1LL;
}
int64_t cachedDurationUs;
bool eos;
if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
- return -1ll;
+ return -1LL;
}
return (jlong) cachedDurationUs;
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 693bd8b..747d4c01 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "android_media_AudioEffect.h"
#include <stdio.h>
@@ -29,6 +28,10 @@
#include <nativehelper/ScopedUtfChars.h>
+#include "android_media_AudioEffect.h"
+#include "android_media_AudioEffectDescriptor.h"
+#include "android_media_AudioErrors.h"
+
using namespace android;
#define AUDIOEFFECT_SUCCESS 0
@@ -49,8 +52,6 @@
jmethodID midPostNativeEvent; // event post callback method
jfieldID fidNativeAudioEffect; // stores in Java the native AudioEffect object
jfieldID fidJniData; // stores in Java additional resources used by the native AudioEffect
- jclass clazzDesc; // AudioEffect.Descriptor class
- jmethodID midDescCstor; // AudioEffect.Descriptor class constructor
};
static fields_t fields;
@@ -226,7 +227,6 @@
ALOGV("android_media_AudioEffect_native_init");
fields.clazzEffect = NULL;
- fields.clazzDesc = NULL;
// Get the AudioEffect class
jclass clazz = env->FindClass(kClassPathName);
@@ -263,23 +263,6 @@
ALOGE("Can't find AudioEffect.%s", "mJniData");
return;
}
-
- clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
- if (clazz == NULL) {
- ALOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class");
- return;
- }
- fields.clazzDesc = (jclass)env->NewGlobalRef(clazz);
-
- fields.midDescCstor
- = env->GetMethodID(
- fields.clazzDesc,
- "<init>",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
- if (fields.midDescCstor == NULL) {
- ALOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor");
- return;
- }
}
@@ -297,12 +280,6 @@
const char *uuidStr = NULL;
effect_descriptor_t desc;
jobject jdesc;
- char str[EFFECT_STRING_LEN_MAX];
- jstring jdescType;
- jstring jdescUuid;
- jstring jdescConnect;
- jstring jdescName;
- jstring jdescImplementor;
ScopedUtfChars opPackageNameStr(env, opPackageName);
@@ -394,41 +371,12 @@
// get the effect descriptor
desc = lpAudioEffect->descriptor();
- AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
- jdescType = env->NewStringUTF(str);
-
- AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
- jdescUuid = env->NewStringUTF(str);
-
- if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- jdescConnect = env->NewStringUTF("Auxiliary");
- } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
- jdescConnect = env->NewStringUTF("Pre Processing");
- } else {
- jdescConnect = env->NewStringUTF("Insert");
- }
-
- jdescName = env->NewStringUTF(desc.name);
- jdescImplementor = env->NewStringUTF(desc.implementor);
-
- jdesc = env->NewObject(fields.clazzDesc,
- fields.midDescCstor,
- jdescType,
- jdescUuid,
- jdescConnect,
- jdescName,
- jdescImplementor);
- env->DeleteLocalRef(jdescType);
- env->DeleteLocalRef(jdescUuid);
- env->DeleteLocalRef(jdescConnect);
- env->DeleteLocalRef(jdescName);
- env->DeleteLocalRef(jdescImplementor);
- if (jdesc == NULL) {
- ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
+ if (convertAudioEffectDescriptorFromNative(env, &jdesc, &desc) != AUDIO_JAVA_SUCCESS) {
goto setup_failure;
}
env->SetObjectArrayElement(javadesc, 0, jdesc);
+ env->DeleteLocalRef(jdesc);
setAudioEffect(env, thiz, lpAudioEffect);
@@ -729,23 +677,16 @@
android_media_AudioEffect_native_queryEffects(JNIEnv *env, jclass clazz __unused)
{
effect_descriptor_t desc;
- char str[EFFECT_STRING_LEN_MAX];
uint32_t totalEffectsCount = 0;
uint32_t returnedEffectsCount = 0;
uint32_t i = 0;
- jstring jdescType;
- jstring jdescUuid;
- jstring jdescConnect;
- jstring jdescName;
- jstring jdescImplementor;
- jobject jdesc;
jobjectArray ret;
if (AudioEffect::queryNumberEffects(&totalEffectsCount) != NO_ERROR) {
return NULL;
}
- jobjectArray temp = env->NewObjectArray(totalEffectsCount, fields.clazzDesc, NULL);
+ jobjectArray temp = env->NewObjectArray(totalEffectsCount, audioEffectDescriptorClass(), NULL);
if (temp == NULL) {
return temp;
}
@@ -757,49 +698,18 @@
goto queryEffects_failure;
}
- if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- jdescConnect = env->NewStringUTF("Auxiliary");
- } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT) {
- jdescConnect = env->NewStringUTF("Insert");
- } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
- jdescConnect = env->NewStringUTF("Pre Processing");
- } else {
+ jobject jdesc;
+ if (convertAudioEffectDescriptorFromNative(env, &jdesc, &desc) != AUDIO_JAVA_SUCCESS) {
continue;
}
-
- AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
- jdescType = env->NewStringUTF(str);
-
- AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
- jdescUuid = env->NewStringUTF(str);
-
- jdescName = env->NewStringUTF(desc.name);
- jdescImplementor = env->NewStringUTF(desc.implementor);
-
- jdesc = env->NewObject(fields.clazzDesc,
- fields.midDescCstor,
- jdescType,
- jdescUuid,
- jdescConnect,
- jdescName,
- jdescImplementor);
- env->DeleteLocalRef(jdescType);
- env->DeleteLocalRef(jdescUuid);
- env->DeleteLocalRef(jdescConnect);
- env->DeleteLocalRef(jdescName);
- env->DeleteLocalRef(jdescImplementor);
- if (jdesc == NULL) {
- ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
- goto queryEffects_failure;
- }
-
env->SetObjectArrayElement(temp, returnedEffectsCount++, jdesc);
- }
+ env->DeleteLocalRef(jdesc);
+ }
if (returnedEffectsCount == 0) {
goto queryEffects_failure;
}
- ret = env->NewObjectArray(returnedEffectsCount, fields.clazzDesc, NULL);
+ ret = env->NewObjectArray(returnedEffectsCount, audioEffectDescriptorClass(), NULL);
if (ret == NULL) {
goto queryEffects_failure;
}
@@ -835,51 +745,11 @@
}
ALOGV("queryDefaultPreProcessing() got %d effects", numEffects);
- jobjectArray ret = env->NewObjectArray(numEffects, fields.clazzDesc, NULL);
- if (ret == NULL) {
- return ret;
- }
+ std::vector<effect_descriptor_t> descVector(descriptors.get(), descriptors.get() + numEffects);
- char str[EFFECT_STRING_LEN_MAX];
- jstring jdescType;
- jstring jdescUuid;
- jstring jdescConnect;
- jstring jdescName;
- jstring jdescImplementor;
- jobject jdesc;
-
- for (uint32_t i = 0; i < numEffects; i++) {
-
- AudioEffect::guidToString(&descriptors[i].type, str, EFFECT_STRING_LEN_MAX);
- jdescType = env->NewStringUTF(str);
- AudioEffect::guidToString(&descriptors[i].uuid, str, EFFECT_STRING_LEN_MAX);
- jdescUuid = env->NewStringUTF(str);
- jdescConnect = env->NewStringUTF("Pre Processing");
- jdescName = env->NewStringUTF(descriptors[i].name);
- jdescImplementor = env->NewStringUTF(descriptors[i].implementor);
-
- jdesc = env->NewObject(fields.clazzDesc,
- fields.midDescCstor,
- jdescType,
- jdescUuid,
- jdescConnect,
- jdescName,
- jdescImplementor);
- env->DeleteLocalRef(jdescType);
- env->DeleteLocalRef(jdescUuid);
- env->DeleteLocalRef(jdescConnect);
- env->DeleteLocalRef(jdescName);
- env->DeleteLocalRef(jdescImplementor);
- if (jdesc == NULL) {
- ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
- env->DeleteLocalRef(ret);
- return NULL;
- }
-
- env->SetObjectArrayElement(ret, i, jdesc);
- }
-
- return ret;
+ jobjectArray ret;
+ convertAudioEffectDescriptorVectorFromNative(env, &ret, descVector);
+ return ret;
}
// ----------------------------------------------------------------------------
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index b7d7b03..45de36e 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -440,6 +440,7 @@
if (lpVisualizer == 0) {
return;
}
+ lpVisualizer->release();
}
// delete the JNI data
VisualizerJniStorage* lpJniStorage =
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 6f2b6c9..38df9b0 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.app.Person;
import android.app.RemoteAction;
import android.content.Context;
import android.os.Bundle;
@@ -31,8 +32,14 @@
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.Deque;
import java.util.List;
import java.util.stream.Collectors;
@@ -50,6 +57,8 @@
private static final int MAX_ACTIONS_PER_LINK = 1;
private static final int MAX_SMART_ACTIONS = 3;
private static final int MAX_SUGGESTED_REPLIES = 3;
+ // TODO: Make this configurable.
+ private static final int MAX_MESSAGES_TO_EXTRACT = 5;
private static final ConversationActions.TypeConfig TYPE_CONFIG =
new ConversationActions.TypeConfig.Builder().setIncludedTypes(
@@ -64,9 +73,6 @@
/**
* Adds action adjustments based on the notification contents.
- *
- * TODO: Once we have a API in {@link TextClassificationManager} to predict smart actions
- * from notification text / message, we can replace most of the code here by consuming that API.
*/
@NonNull
ArrayList<Notification.Action> suggestActions(@Nullable Context context,
@@ -84,9 +90,13 @@
if (tcm == null) {
return EMPTY_ACTION_LIST;
}
+ List<ConversationActions.Message> messages = extractMessages(entry.getNotification());
+ if (messages.isEmpty()) {
+ return EMPTY_ACTION_LIST;
+ }
+ // TODO: Move to TextClassifier.suggestConversationActions once it is ready.
return suggestActionsFromText(
- tcm,
- getMostSalientActionText(entry.getNotification()), MAX_SMART_ACTIONS);
+ tcm, messages.get(messages.size() - 1).getText(), MAX_SMART_ACTIONS);
}
ArrayList<CharSequence> suggestReplies(@Nullable Context context,
@@ -104,14 +114,12 @@
if (tcm == null) {
return EMPTY_REPLY_LIST;
}
- CharSequence text = getMostSalientActionText(entry.getNotification());
- ConversationActions.Message message =
- new ConversationActions.Message.Builder()
- .setText(text)
- .build();
-
+ List<ConversationActions.Message> messages = extractMessages(entry.getNotification());
+ if (messages.isEmpty()) {
+ return EMPTY_REPLY_LIST;
+ }
ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
+ new ConversationActions.Request.Builder(messages)
.setMaxSuggestions(MAX_SUGGESTED_REPLIES)
.setHints(HINTS)
.setTypeConfig(TYPE_CONFIG)
@@ -140,10 +148,6 @@
if (!Process.myUserHandle().equals(entry.getSbn().getUser())) {
return false;
}
- if (notification.actions != null
- && notification.actions.length >= Notification.MAX_ACTION_BUTTONS) {
- return false;
- }
if ((notification.flags & FLAG_MASK_INELGIBILE_FOR_ACTIONS) != 0) {
return false;
}
@@ -176,21 +180,41 @@
/** Returns the text most salient for action extraction in a notification. */
@Nullable
- private CharSequence getMostSalientActionText(@NonNull Notification notification) {
- /* If it's messaging style, use the most recent message. */
- // TODO: Use the last few X messages instead and take the Person object into consideration.
+ private List<ConversationActions.Message> extractMessages(@NonNull Notification notification) {
Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
- if (messages != null && messages.length != 0) {
- Bundle lastMessage = (Bundle) messages[messages.length - 1];
- CharSequence lastMessageText =
- lastMessage.getCharSequence(Notification.MessagingStyle.Message.KEY_TEXT);
- if (!TextUtils.isEmpty(lastMessageText)) {
- return lastMessageText;
+ if (messages == null || messages.length == 0) {
+ return Arrays.asList(new ConversationActions.Message.Builder(
+ ConversationActions.Message.PERSON_USER_REMOTE)
+ .setText(notification.extras.getCharSequence(Notification.EXTRA_TEXT))
+ .build());
+ }
+ Person localUser = notification.extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
+ Deque<ConversationActions.Message> extractMessages = new ArrayDeque<>();
+ for (int i = messages.length - 1; i >= 0; i--) {
+ Notification.MessagingStyle.Message message =
+ Notification.MessagingStyle.Message.getMessageFromBundle((Bundle) messages[i]);
+ if (message == null) {
+ continue;
+ }
+ Person senderPerson = message.getSenderPerson();
+ // Skip encoding once the sender is missing as it is important to distinguish
+ // local user and remote user when generating replies.
+ if (senderPerson == null) {
+ break;
+ }
+ Person author = localUser != null && localUser.equals(senderPerson)
+ ? ConversationActions.Message.PERSON_USER_LOCAL : senderPerson;
+ extractMessages.push(new ConversationActions.Message.Builder(author)
+ .setText(message.getText())
+ .setReferenceTime(
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(message.getTimestamp()),
+ ZoneOffset.systemDefault()))
+ .build());
+ if (extractMessages.size() >= MAX_MESSAGES_TO_EXTRACT) {
+ break;
}
}
-
- // Fall back to using the normal text.
- return notification.extras.getCharSequence(Notification.EXTRA_TEXT);
+ return new ArrayList<>(extractMessages);
}
/** Returns a list of actions to act on entities in a given piece of text. */
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
new file mode 100644
index 0000000..60d31fc
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
@@ -0,0 +1,236 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.ext.services.notification;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.Person;
+import android.content.Context;
+import android.os.Process;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.textclassifier.ConversationActions;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+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.time.Instant;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import javax.annotation.Nullable;
+
+@RunWith(AndroidJUnit4.class)
+public class SmartActionHelperTest {
+
+ private SmartActionsHelper mSmartActionsHelper = new SmartActionsHelper();
+ private Context mContext;
+ @Mock private TextClassifier mTextClassifier;
+ @Mock private NotificationEntry mNotificationEntry;
+ @Mock private StatusBarNotification mStatusBarNotification;
+ private Notification.Builder mNotificationBuilder;
+ private AssistantSettings mSettings;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getTargetContext();
+
+ mContext.getSystemService(TextClassificationManager.class)
+ .setTextClassifier(mTextClassifier);
+ when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
+ .thenReturn(new ConversationActions(Collections.emptyList()));
+
+ when(mNotificationEntry.getSbn()).thenReturn(mStatusBarNotification);
+ // The notification is eligible to have smart suggestions.
+ when(mNotificationEntry.hasInlineReply()).thenReturn(true);
+ when(mNotificationEntry.isMessaging()).thenReturn(true);
+ when(mStatusBarNotification.getPackageName()).thenReturn("random.app");
+ when(mStatusBarNotification.getUser()).thenReturn(Process.myUserHandle());
+ mNotificationBuilder = new Notification.Builder(mContext, "channel");
+ mSettings = AssistantSettings.createForTesting(
+ null, null, Process.myUserHandle().getIdentifier(), null);
+ mSettings.mGenerateActions = true;
+ mSettings.mGenerateReplies = true;
+ }
+
+ @Test
+ public void testSuggestReplies_notMessagingApp() {
+ when(mNotificationEntry.isMessaging()).thenReturn(false);
+ ArrayList<CharSequence> textReplies =
+ mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+ assertThat(textReplies).isEmpty();
+ }
+
+ @Test
+ public void testSuggestReplies_noInlineReply() {
+ when(mNotificationEntry.hasInlineReply()).thenReturn(false);
+ ArrayList<CharSequence> textReplies =
+ mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+ assertThat(textReplies).isEmpty();
+ }
+
+ @Test
+ public void testSuggestReplies_nonMessageStyle() {
+ Notification notification = mNotificationBuilder.setContentText("Where are you?").build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ List<ConversationActions.Message> messages = getMessagesInRequest();
+ assertThat(messages).hasSize(1);
+ MessageSubject.assertThat(messages.get(0)).hasText("Where are you?");
+ }
+
+ @Test
+ public void testSuggestReplies_messageStyle() {
+ Person me = new Person.Builder().setName("Me").build();
+ Person userA = new Person.Builder().setName("A").build();
+ Person userB = new Person.Builder().setName("B").build();
+ Notification.MessagingStyle style =
+ new Notification.MessagingStyle(me)
+ .addMessage("firstMessage", 1000, (Person) null)
+ .addMessage("secondMessage", 2000, me)
+ .addMessage("thirdMessage", 3000, userA)
+ .addMessage("fourthMessage", 4000, userB);
+ Notification notification =
+ mNotificationBuilder
+ .setContentText("You have three new messages")
+ .setStyle(style)
+ .build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ List<ConversationActions.Message> messages = getMessagesInRequest();
+ assertThat(messages).hasSize(3);
+
+ ConversationActions.Message secondMessage = messages.get(0);
+ MessageSubject.assertThat(secondMessage).hasText("secondMessage");
+ MessageSubject.assertThat(secondMessage)
+ .hasPerson(ConversationActions.Message.PERSON_USER_LOCAL);
+ MessageSubject.assertThat(secondMessage)
+ .hasReferenceTime(createZonedDateTimeFromMsUtc(2000));
+
+ ConversationActions.Message thirdMessage = messages.get(1);
+ MessageSubject.assertThat(thirdMessage).hasText("thirdMessage");
+ MessageSubject.assertThat(thirdMessage).hasPerson(userA);
+ MessageSubject.assertThat(thirdMessage)
+ .hasReferenceTime(createZonedDateTimeFromMsUtc(3000));
+
+ ConversationActions.Message fourthMessage = messages.get(2);
+ MessageSubject.assertThat(fourthMessage).hasText("fourthMessage");
+ MessageSubject.assertThat(fourthMessage).hasPerson(userB);
+ MessageSubject.assertThat(fourthMessage)
+ .hasReferenceTime(createZonedDateTimeFromMsUtc(4000));
+ }
+
+ @Test
+ public void testSuggestReplies_messageStyle_noPerson() {
+ Person me = new Person.Builder().setName("Me").build();
+ Notification.MessagingStyle style =
+ new Notification.MessagingStyle(me).addMessage("message", 1000, (Person) null);
+ Notification notification =
+ mNotificationBuilder
+ .setContentText("You have one new message")
+ .setStyle(style)
+ .build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+
+ verify(mTextClassifier, never())
+ .suggestConversationActions(any(ConversationActions.Request.class));
+ }
+
+ private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneOffset.systemDefault());
+ }
+
+ private List<ConversationActions.Message> getMessagesInRequest() {
+ mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+
+ ArgumentCaptor<ConversationActions.Request> argumentCaptor =
+ ArgumentCaptor.forClass(ConversationActions.Request.class);
+ verify(mTextClassifier).suggestConversationActions(argumentCaptor.capture());
+ ConversationActions.Request request = argumentCaptor.getValue();
+ return request.getConversation();
+ }
+
+ private static final class MessageSubject
+ extends Subject<MessageSubject, ConversationActions.Message> {
+
+ private static final SubjectFactory<MessageSubject, ConversationActions.Message> FACTORY =
+ new SubjectFactory<MessageSubject, ConversationActions.Message>() {
+ @Override
+ public MessageSubject getSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull ConversationActions.Message subject) {
+ return new MessageSubject(failureStrategy, subject);
+ }
+ };
+
+ private MessageSubject(
+ FailureStrategy failureStrategy, @Nullable ConversationActions.Message subject) {
+ super(failureStrategy, subject);
+ }
+
+ private void hasText(String text) {
+ if (!Objects.equals(text, getSubject().getText().toString())) {
+ failWithBadResults("has text", text, "has", getSubject().getText());
+ }
+ }
+
+ private void hasPerson(Person person) {
+ if (!Objects.equals(person, getSubject().getAuthor())) {
+ failWithBadResults("has author", person, "has", getSubject().getAuthor());
+ }
+ }
+
+ private void hasReferenceTime(ZonedDateTime referenceTime) {
+ if (!Objects.equals(referenceTime, getSubject().getReferenceTime())) {
+ failWithBadResults(
+ "has reference time",
+ referenceTime,
+ "has",
+ getSubject().getReferenceTime());
+ }
+ }
+
+ private static MessageSubject assertThat(ConversationActions.Message message) {
+ return assertAbout(FACTORY).that(message);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index eb9ec82..18b8662 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -15,6 +15,7 @@
<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
<uses-permission android:name="com.google.android.permission.INSTALL_WEARABLE_PACKAGES" />
diff --git a/packages/PackageInstaller/res/layout/uninstall_content_view.xml b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
new file mode 100644
index 0000000..5ecb614
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
@@ -0,0 +1,47 @@
+<?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.
+ -->
+
+<!-- Check box that is displayed in the activity resolver UI for the user
+ to make their selection the preferred activity. -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="?android:attr/alertDialogTheme"
+ android:orientation="vertical"
+ android:paddingTop="8dp"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:clipToPadding="false">
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+ <CheckBox
+ android:id="@+id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="-8dp"
+ android:paddingLeft="8sp"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index d751c42..01f3e5b 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -22,17 +22,17 @@
<string name="cancel" msgid="1018267193425558088">"रद्द करा"</string>
<string name="installing" msgid="4921993079741206516">"इंस्टॉल होत आहे…"</string>
<string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> इंस्टॉल होत आहे…"</string>
- <string name="install_done" msgid="5987363587661783896">"अॅप इंस्टॉल झाले."</string>
+ <string name="install_done" msgid="5987363587661783896">"अॅप इंस्टॉल झाले."</string>
<string name="install_confirm_question" msgid="8176284075816604590">"तुम्हाला हे अॅप्लिकेशन इंस्टॉल करायचे आहे का?"</string>
<string name="install_confirm_question_update" msgid="7942235418781274635">"तुम्हाच्या विद्यमान अॅप्लिकेशनवर अपडेट इंस्टॉल करायचे आहे का? तुमचा विद्यमान डेटा गमावणार नाही."</string>
<string name="install_confirm_question_update_system" msgid="4713001702777910263">"तुम्हाला या बिल्ट-इन अॅप्लिकेशनवर अपडेट इंस्टॉल करायचे आहे का? तुमचा विद्यमान डेटा गमावणार नाही."</string>
- <string name="install_failed" msgid="5777824004474125469">"अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed" msgid="5777824004474125469">"अॅप इंस्टॉल झाले नाही."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"पॅकेज इंस्टॉल होण्यापासून ब्लॉक केले होते."</string>
- <string name="install_failed_conflict" msgid="3493184212162521426">"पॅकेजचा विद्यमान पॅकेजशी विरोध असल्याने अॅप इंस्टॉल झाले नाही."</string>
- <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"तुमच्या टॅबलेटशी कंपॅटिबल नसल्याने अॅप इंस्टॉल झाले नाही."</string>
- <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"हे अॅप तुमच्या टीव्हीशी कंपॅटिबल नाही."</string>
- <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"तुमच्या फोनशी कंपॅटिबल नसल्याने अॅप इंस्टॉल झाले नाही."</string>
- <string name="install_failed_invalid_apk" msgid="8581007676422623930">"पॅकेज अयोग्य असल्याचे दिसत असल्याने अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_conflict" msgid="3493184212162521426">"पॅकेजचा विद्यमान पॅकेजशी विरोध असल्याने अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"तुमच्या टॅबलेटशी कंपॅटिबल नसल्याने अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"हे अॅप तुमच्या टीव्हीशी कंपॅटिबल नाही."</string>
+ <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"तुमच्या फोनशी कंपॅटिबल नसल्याने अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_invalid_apk" msgid="8581007676422623930">"पॅकेज अयोग्य असल्याचे दिसत असल्याने अॅप इंस्टॉल झाले नाही."</string>
<string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"<xliff:g id="APP_NAME">%1$s</xliff:g> तुमच्या टॅबलेटवर इंस्टॉल केले जाऊ शकत नाही."</string>
<string name="install_failed_msg" product="tv" msgid="1920009940048975221">"<xliff:g id="APP_NAME">%1$s</xliff:g> तुमच्या टीव्हीवर इंस्टॉल केले जाऊ शकत नाही."</string>
<string name="install_failed_msg" product="default" msgid="6484461562647915707">"<xliff:g id="APP_NAME">%1$s</xliff:g> तुमच्या फोनवर इंस्टॉल केले जाऊ शकत नाही."</string>
@@ -44,20 +44,20 @@
<string name="manage_applications" msgid="5400164782453975580">"अॅप्स व्यवस्थापन"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"जागा संपली"</string>
<string name="out_of_space_dlg_text" msgid="8727714096031856231">"<xliff:g id="APP_NAME">%1$s</xliff:g> इंस्टॉल केले जाऊ शकत नाही. काही जागा मोकळी करा आणि पुन्हा प्रयत्न करा."</string>
- <string name="app_not_found_dlg_title" msgid="5107924008597470285">"अॅप आढळले नाही"</string>
- <string name="app_not_found_dlg_text" msgid="5219983779377811611">"इंस्टॉल केलेल्या अॅप्सच्या सूचीमध्ये अॅप आढळले नाही."</string>
+ <string name="app_not_found_dlg_title" msgid="5107924008597470285">"अॅप आढळले नाही"</string>
+ <string name="app_not_found_dlg_text" msgid="5219983779377811611">"इंस्टॉल केलेल्या अॅप्सच्या सूचीमध्ये अॅप आढळले नाही."</string>
<string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"अनुमती नाही"</string>
<string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"हे अनइंस्टॉल करण्याची विद्यमान वापरकर्त्यास अनुमती नाही."</string>
<string name="generic_error_dlg_title" msgid="5863195085927067752">"एरर"</string>
- <string name="generic_error_dlg_text" msgid="5287861443265795232">"अॅप अनइंस्टॉल करणे शक्य झाले नाही."</string>
- <string name="uninstall_application_title" msgid="4045420072401428123">"अॅप अनइंस्टॉल करा"</string>
+ <string name="generic_error_dlg_text" msgid="5287861443265795232">"अॅप अनइंस्टॉल करणे शक्य झाले नाही."</string>
+ <string name="uninstall_application_title" msgid="4045420072401428123">"अॅप अनइंस्टॉल करा"</string>
<string name="uninstall_update_title" msgid="824411791011583031">"अपडेट अनइंस्टॉल करा"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> खालील अॅपचा भाग आहे:"</string>
- <string name="uninstall_application_text" msgid="3816830743706143980">"तुम्हाला हे अॅप अनइंस्टॉल करायचे आहे का?"</string>
- <string name="uninstall_application_text_all_users" msgid="575491774380227119">"तुम्हाला हे अॅप "<b>"सर्व"</b>" वापरकर्त्यांसाठी अनइंस्टॉल करायचे आहे का? अॅप्लिकेशन आणि त्याचा डेटा डिव्हाइसवरील "<b>"सर्व"</b>" वापरकर्त्यांकडून काढला जाईल."</string>
+ <string name="uninstall_application_text" msgid="3816830743706143980">"तुम्हाला हे अॅप अनइंस्टॉल करायचे आहे का?"</string>
+ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"तुम्हाला हे अॅप "<b>"सर्व"</b>" वापरकर्त्यांसाठी अनइंस्टॉल करायचे आहे का? अॅप्लिकेशन आणि त्याचा डेटा डिव्हाइसवरील "<b>"सर्व"</b>" वापरकर्त्यांकडून काढला जाईल."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"तुम्हाला <xliff:g id="USERNAME">%1$s</xliff:g> वापरकर्त्यासाठी हे अॅप अनइंस्टॉल करायचे आहे का?"</string>
- <string name="uninstall_update_text" msgid="863648314632448705">"फॅक्टरी आवृत्तीसह हे अॅप बदलायचे का? सर्व डेटा काढला जाईल."</string>
- <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"फॅक्टरी आवृत्तीसह हे अॅप बदलायचे? सर्व डेटा काढला जाईल. हे कार्य प्रोफाइल असलेल्यांसह या डिव्हाइसच्या सर्व वापरकर्त्यांना प्रभावित करते."</string>
+ <string name="uninstall_update_text" msgid="863648314632448705">"फॅक्टरी आवृत्तीसह हे अॅप बदलायचे का? सर्व डेटा काढला जाईल."</string>
+ <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"फॅक्टरी आवृत्तीसह हे अॅप बदलायचे? सर्व डेटा काढला जाईल. हे कार्य प्रोफाइल असलेल्यांसह या डिव्हाइसच्या सर्व वापरकर्त्यांना प्रभावित करते."</string>
<string name="uninstalling_notification_channel" msgid="840153394325714653">"अनइंस्टॉल रन होत आहेत"</string>
<string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"अनइंस्टॉल करता आले नाही"</string>
<string name="uninstalling" msgid="8709566347688966845">"अनइंस्टॉल करत आहे…"</string>
@@ -68,7 +68,7 @@
<string name="uninstall_failed_app" msgid="5506028705017601412">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> अनइंस्टॉल करता आले नाही."</string>
<string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"अॅक्टिव्ह डिव्हाइस प्रशासक अॅप अनइंस्टॉल करू शकत नाही"</string>
<string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"<xliff:g id="USERNAME">%1$s</xliff:g> साठी अॅक्टिव्ह डिव्हाइस प्रशासक अॅप अनइंस्टॉल करू शकत नाही"</string>
- <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"हे अॅप काही वापरकर्ते किंवा प्रोफाइलसाठी आवश्यक आहे आणि इतरांसाठी अनइंस्टॉल करण्यात आले"</string>
+ <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"हे अॅप काही वापरकर्ते किंवा प्रोफाइलसाठी आवश्यक आहे आणि इतरांसाठी अनइंस्टॉल करण्यात आले"</string>
<string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"तुमच्या प्रोफाइलसाठी हे अॅप आवश्यक आहे आणि अनइंस्टॉल केले जाऊ शकत नाही."</string>
<string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"तुमच्या डिव्हाइस प्रशासकास हे अॅप आवश्यक आहे आणि ते अनइंस्टॉल केले जाऊ शकत नाही."</string>
<string name="manage_device_administrators" msgid="3092696419363842816">"डिव्हाइस प्रशासक अॅप्स व्यवस्थापित करा"</string>
@@ -77,18 +77,18 @@
<string name="Parse_error_dlg_text" msgid="1661404001063076789">"पॅकेज पार्स करण्यात समस्या आली."</string>
<string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string>
<string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"इंस्टॉल करा/अनइंस्टॉल करा क्रिया Wear वर सपोर्ट करत नाहीत."</string>
- <string name="message_staging" msgid="8032722385658438567">"अॅप सुरुवातीच्या स्थितीत आहे…"</string>
+ <string name="message_staging" msgid="8032722385658438567">"अॅप सुरुवातीच्या स्थितीत आहे…"</string>
<string name="app_name_unknown" msgid="6881210203354323926">"अज्ञात"</string>
<string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"तुमच्या सुरक्षिततेसाठी, तुमच्या टॅबलेटला या स्रोताकडील अज्ञात अॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
<string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"तुमच्या सुरक्षिततेसाठी, तुमच्या टीव्हीला या स्रोताकडील अज्ञात अॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
<string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"तुमच्या सुरक्षिततेसाठी, तुमच्या फोनला या स्रोताकडील अज्ञात अॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"तुमचा फोन आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने होणार्या तुमच्या फोनचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
- <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"तुमचा टॅबलेट आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने तुमच्या टॅबलेटचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
- <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"तुमचा टीव्ही आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने तुमच्या टीव्हीचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"तुमचा फोन आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने होणार्या तुमच्या फोनचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"तुमचा टॅबलेट आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने तुमच्या टॅबलेटचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"तुमचा टीव्ही आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने तुमच्या टीव्हीचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"सुरू ठेवा"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"सेटिंग्ज"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"wear अॅप्स इंस्टॉल/अनइंस्टॉल करत आहे"</string>
- <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"अॅप इंस्टॉल सूचना"</string>
+ <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"अॅप इंस्टॉल सूचना"</string>
<string name="notification_installation_success_message" msgid="6450467996056038442">"यशस्वीरित्या इंस्टॉल केले"</string>
<string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" यशस्वीरित्या इंस्टॉल झाले"</string>
</resources>
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index 0f065ab..1d8747a1 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -119,6 +119,8 @@
<string name="uninstall_update_text">Replace this app with the factory version? All data will be removed.</string>
<!-- [CHAR LIMIT=none] -->
<string name="uninstall_update_text_multiuser">Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.</string>
+ <!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] -->
+ <string name="uninstall_remove_contributed_files">Also remove <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of associated media files.</string>
<!-- Label for the notification channel containing notifications for current uninstall operations [CHAR LIMIT=40] -->
<string name="uninstalling_notification_channel">Running uninstalls</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
index 1c0aec1..d13bb65 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
@@ -50,6 +50,8 @@
"com.android.packageinstaller.ACTION_UNINSTALL_COMMIT";
static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL";
+ static final String EXTRA_CLEAR_CONTRIBUTED_FILES =
+ "com.android.packageinstaller.extra.CLEAR_CONTRIBUTED_FILES";
private int mUninstallId;
private ApplicationInfo mAppInfo;
@@ -72,6 +74,8 @@
if (savedInstanceState == null) {
boolean allUsers = getIntent().getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS,
false);
+ boolean clearContributedFiles = getIntent().getBooleanExtra(
+ EXTRA_CLEAR_CONTRIBUTED_FILES, false);
UserHandle user = getIntent().getParcelableExtra(Intent.EXTRA_USER);
// Show dialog, which is the whole UI
@@ -95,12 +99,15 @@
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId,
broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0;
+ flags |= clearContributedFiles ? PackageManager.DELETE_CONTRIBUTED_MEDIA : 0;
+
try {
ActivityThread.getPackageManager().getPackageInstaller().uninstall(
new VersionedPackage(mAppInfo.packageName,
PackageManager.VERSION_CODE_HIGHEST),
- getPackageName(), allUsers ? PackageManager.DELETE_ALL_USERS : 0,
- pendingIntent.getIntentSender(), user.getIdentifier());
+ getPackageName(), flags, pendingIntent.getIntentSender(),
+ user.getIdentifier());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 1a01dc0..0fa8c9a 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -285,7 +285,7 @@
fragment.show(ft, "dialog");
}
- public void startUninstallProgress() {
+ public void startUninstallProgress(boolean clearContributedFiles) {
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager());
@@ -310,6 +310,8 @@
newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
newIntent.putExtra(UninstallUninstalling.EXTRA_APP_LABEL, label);
+ newIntent.putExtra(UninstallUninstalling.EXTRA_CLEAR_CONTRIBUTED_FILES,
+ clearContributedFiles);
newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
if (returnResult) {
@@ -358,12 +360,14 @@
try {
Log.i(TAG, "Uninstalling extras=" + broadcastIntent.getExtras());
+ int flags = mDialogInfo.allUsers ? PackageManager.DELETE_ALL_USERS : 0;
+ flags |= clearContributedFiles ? PackageManager.DELETE_CONTRIBUTED_MEDIA : 0;
+
ActivityThread.getPackageManager().getPackageInstaller().uninstall(
new VersionedPackage(mDialogInfo.appInfo.packageName,
PackageManager.VERSION_CODE_HIGHEST),
- getPackageName(), mDialogInfo.allUsers
- ? PackageManager.DELETE_ALL_USERS : 0,
- pendingIntent.getIntentSender(), mDialogInfo.user.getIdentifier());
+ getPackageName(), flags, pendingIntent.getIntentSender(),
+ mDialogInfo.user.getIdentifier());
} catch (Exception e) {
notificationManager.cancel(uninstallId);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index e0ca74e..e4e1275 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -16,6 +16,9 @@
package com.android.packageinstaller.handheld;
+import static android.text.format.Formatter.formatFileSize;
+
+import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -24,14 +27,41 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Bundle;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.MediaStore;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.TextView;
import com.android.packageinstaller.R;
import com.android.packageinstaller.UninstallerActivity;
+import java.io.IOException;
+import java.util.List;
+
public class UninstallAlertDialogFragment extends DialogFragment implements
DialogInterface.OnClickListener {
+ private CheckBox mClearContributedFiles;
+
+ /**
+ * Get number of bytes of the combined files contributed by the package.
+ *
+ * @param pkg The package that might have contibuted files.
+ * @param user The user the package belongs to.
+ *
+ * @return The number of bytes.
+ */
+ private long getContributedMediaSize(@NonNull String pkg, @NonNull UserHandle user) {
+ try {
+ return MediaStore.getContributedMediaSize(getContext(), pkg, user);
+ } catch (IOException e) {
+ return 0;
+ }
+ }
+
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final PackageManager pm = getActivity().getPackageManager();
@@ -76,14 +106,44 @@
dialogBuilder.setTitle(appLabel);
dialogBuilder.setPositiveButton(android.R.string.ok, this);
dialogBuilder.setNegativeButton(android.R.string.cancel, this);
- dialogBuilder.setMessage(messageBuilder.toString());
+
+ String pkg = dialogInfo.appInfo.packageName;
+ long contributedFileSize = 0;
+ if (dialogInfo.allUsers) {
+ List<UserInfo> users = userManager.getUsers();
+
+ int numUsers = users.size();
+ for (int i = 0; i < numUsers; i++) {
+ UserHandle user = UserHandle.of(users.get(i).id);
+
+ contributedFileSize += getContributedMediaSize(pkg, user);
+ }
+ } else {
+ contributedFileSize = getContributedMediaSize(pkg, dialogInfo.user);
+ }
+
+ if (contributedFileSize == 0) {
+ dialogBuilder.setMessage(messageBuilder.toString());
+ } else {
+ LayoutInflater inflater = getContext().getSystemService(LayoutInflater.class);
+ ViewGroup content = (ViewGroup) inflater.inflate(R.layout.uninstall_content_view, null);
+
+ ((TextView) content.requireViewById(R.id.message)).setText(messageBuilder.toString());
+ mClearContributedFiles = content.requireViewById(R.id.checkbox);
+ mClearContributedFiles.setText(getString(R.string.uninstall_remove_contributed_files,
+ formatFileSize(getContext(), contributedFileSize)));
+
+ dialogBuilder.setView(content);
+ }
+
return dialogBuilder.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == Dialog.BUTTON_POSITIVE) {
- ((UninstallerActivity) getActivity()).startUninstallProgress();
+ ((UninstallerActivity) getActivity()).startUninstallProgress(
+ mClearContributedFiles != null && mClearContributedFiles.isChecked());
} else {
((UninstallerActivity) getActivity()).dispatchAborted();
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
index 828e5db..21d25f5 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
@@ -99,7 +99,7 @@
public void onGuidedActionClicked(GuidedAction action) {
if (isAdded()) {
if (action.getId() == GuidedAction.ACTION_ID_OK) {
- ((UninstallerActivity) getActivity()).startUninstallProgress();
+ ((UninstallerActivity) getActivity()).startUninstallProgress(false);
getActivity().finish();
} else {
((UninstallerActivity) getActivity()).dispatchAborted();
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 8ebb182..5b09b29 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -184,7 +184,7 @@
<string name="category_work" msgid="8699184680584175622">"कार्य"</string>
<string name="development_settings_title" msgid="215179176067683667">"डेव्हलपर पर्याय"</string>
<string name="development_settings_enable" msgid="542530994778109538">"डेव्हलपर पर्याय सुरू करा"</string>
- <string name="development_settings_summary" msgid="1815795401632854041">"अॅप विकासासाठी पर्याय सेट करा"</string>
+ <string name="development_settings_summary" msgid="1815795401632854041">"अॅप विकासासाठी पर्याय सेट करा"</string>
<string name="development_settings_not_available" msgid="4308569041701535607">"या वापरकर्त्यासाठी डेव्हलपर पर्याय उपलब्ध नाहीत"</string>
<string name="vpn_settings_not_available" msgid="956841430176985598">"या वापरकर्त्यासाठी VPN सेटिंग्ज उपलब्ध नाहीत"</string>
<string name="tethering_settings_not_available" msgid="6765770438438291012">"या वापरकर्त्यासाठी टेदरिंग सेटिंग्ज उपलब्ध नाहीत"</string>
@@ -202,9 +202,9 @@
<string name="oem_unlock_enable_summary" msgid="4720281828891618376">"बूटलोडर अनलॉक करण्यासाठी अनुमती द्या"</string>
<string name="confirm_enable_oem_unlock_title" msgid="4802157344812385674">"OEM अनलॉक करण्यास अनुमती द्यायची?"</string>
<string name="confirm_enable_oem_unlock_text" msgid="5517144575601647022">"चेतावणी: हे सेटिंग चालू असताना या डिव्हाइस वर डिव्हाइस संरक्षण वैशिष्ट्ये काम करणार नाहीत."</string>
- <string name="mock_location_app" msgid="7966220972812881854">"बनावट स्थान अॅप निवडा"</string>
- <string name="mock_location_app_not_set" msgid="809543285495344223">"कोणताही बनावट स्थान अॅप सेट केला नाही"</string>
- <string name="mock_location_app_set" msgid="8966420655295102685">"बनावट स्थान अॅप: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="mock_location_app" msgid="7966220972812881854">"बनावट स्थान अॅप निवडा"</string>
+ <string name="mock_location_app_not_set" msgid="809543285495344223">"कोणताही बनावट स्थान अॅप सेट केला नाही"</string>
+ <string name="mock_location_app_set" msgid="8966420655295102685">"बनावट स्थान अॅप: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="7044075693643009662">"नेटवर्किंग"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"वायरलेस डिस्प्ले प्रमाणीकरण"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"वाय-फाय व्हर्बोझ लॉगिंग सुरू करा"</string>
@@ -261,11 +261,11 @@
<string name="bluetooth_show_devices_without_names_summary" msgid="2351196058115755520">"नावांशिवाय ब्लूटूथ डीव्हाइस (फक्त MAC पत्ते) दाखवले जातील"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="6031284410786545957">"रिमोट डिव्हाइसमध्ये सहन न होणारा मोठा आवाज किंवा नियंत्रणाचा अभाव यासारखी आवाजाची समस्या असल्यास ब्लूटूथ संपूर्ण आवाज वैशिष्ट्य बंद करते."</string>
<string name="enable_terminal_title" msgid="95572094356054120">"स्थानिक टर्मिनल"</string>
- <string name="enable_terminal_summary" msgid="67667852659359206">"स्थानिक शेल प्रवेश देणारा टर्मिनल अॅप सुरू करा"</string>
+ <string name="enable_terminal_summary" msgid="67667852659359206">"स्थानिक शेल प्रवेश देणारा टर्मिनल अॅप सुरू करा"</string>
<string name="hdcp_checking_title" msgid="8605478913544273282">"HDCP तपासणी"</string>
<string name="hdcp_checking_dialog_title" msgid="5141305530923283">"HDCP तपासणी वर्तन सेट करा"</string>
<string name="debug_debugging_category" msgid="6781250159513471316">"डीबग करणे"</string>
- <string name="debug_app" msgid="8349591734751384446">"डीबग अॅप निवडा"</string>
+ <string name="debug_app" msgid="8349591734751384446">"डीबग अॅप निवडा"</string>
<string name="debug_app_not_set" msgid="718752499586403499">"कोणतेही डीबग अॅप्लिकेशन सेट नाही"</string>
<string name="debug_app_set" msgid="2063077997870280017">"अॅप्लिकेशन डीबग करत आहे: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="select_application" msgid="5156029161289091703">"अॅप्लिकेशन निवडा"</string>
@@ -315,7 +315,7 @@
<string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"वापरकर्त्याने प्रत्येक अॅक्टिव्हिटी सोडताच ती नष्ट करा"</string>
<string name="app_process_limit_title" msgid="4280600650253107163">"पार्श्वभूमी प्रक्रिया मर्यादा"</string>
<string name="show_all_anrs" msgid="4924885492787069007">"बॅकग्राउंड ANR दाखवा"</string>
- <string name="show_all_anrs_summary" msgid="6636514318275139826">"बॅकग्राउंड अॅप्ससाठी अॅप प्रतिसाद देत नाही दाखवते"</string>
+ <string name="show_all_anrs_summary" msgid="6636514318275139826">"बॅकग्राउंड अॅप्ससाठी अॅप प्रतिसाद देत नाही दाखवते"</string>
<string name="show_notification_channel_warnings" msgid="1399948193466922683">"सूचना चॅनेल चेतावण्या दाखवा"</string>
<string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"एखादे अॅप वैध चॅनेलशिवाय सूचना पोस्ट करते तेव्हा स्क्रीनवर चेतावणी देते"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"बाह्यवर अॅप्सना अनुमती देण्याची सक्ती करा"</string>
@@ -343,7 +343,7 @@
<string name="inactive_apps_title" msgid="9042996804461901648">"स्टँडबाय अॅप्स"</string>
<string name="inactive_app_inactive_summary" msgid="5091363706699855725">"निष्क्रिय. टॉगल करण्यासाठी टॅप करा."</string>
<string name="inactive_app_active_summary" msgid="4174921824958516106">"सक्रिय. टॉगल करण्यासाठी टॅप करा."</string>
- <string name="standby_bucket_summary" msgid="6567835350910684727">"अॅप स्टँडबाय स्थिती: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
+ <string name="standby_bucket_summary" msgid="6567835350910684727">"अॅप स्टँडबाय स्थिती: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
<string name="runningservices_settings_title" msgid="8097287939865165213">"चालू सेवा"</string>
<string name="runningservices_settings_summary" msgid="854608995821032748">"सध्या चालत असलेल्या सेवा पहा आणि नियंत्रित करा"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"वेबदृश्य अंमलबजावणी"</string>
@@ -422,7 +422,7 @@
<string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"सिस्टम भाषा वापरा"</string>
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> साठी सेटिंग्ज उघडण्यात अयशस्वी"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"ही इनपुट पद्धत पासवर्ड आणि क्रेडिट कार्ड नंबर यासह, तुम्ही टाइप करता तो सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. ही <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> अॅपवरून येते. ही इनपुट पद्धत वापरायची?"</string>
- <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"टीप: रीबूट केल्यानंतर, तुम्ही तुमचा फोन अनलॉक करे पर्यंत हे अॅप सुरू होऊ शकत नाही"</string>
+ <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"टीप: रीबूट केल्यानंतर, तुम्ही तुमचा फोन अनलॉक करे पर्यंत हे अॅप सुरू होऊ शकत नाही"</string>
<string name="ims_reg_title" msgid="7609782759207241443">"IMS नोंदणी स्थिती"</string>
<string name="ims_reg_status_registered" msgid="933003316932739188">"नोंदवलेले"</string>
<string name="ims_reg_status_not_registered" msgid="6529783773485229486">"नोंदवलेले नाही"</string>
@@ -449,6 +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="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 ae9c5f2..e810ba2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -40,7 +40,7 @@
<string name="connected_via_passpoint" msgid="2826205693803088747">"Tilkoblet via %1$s"</string>
<string name="available_via_passpoint" msgid="1617440946846329613">"Tilgjengelig via %1$s"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Tilkoblet – ingen Internett-tilgang"</string>
- <string name="wifi_status_no_internet" msgid="5784710974669608361">"Ingen Internett-tilkobling"</string>
+ <string name="wifi_status_no_internet" msgid="5784710974669608361">"Ingen internettilkobling"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Pålogging kreves"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Tilgangspunktet er midlertidig fullt"</string>
<string name="connected_via_carrier" msgid="7583780074526041912">"Tilkoblet via %1$s"</string>
@@ -75,7 +75,7 @@
<string name="bluetooth_profile_pan" msgid="3391606497945147673">"Internett-tilgang"</string>
<string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kontaktdeling"</string>
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Bruk til kontaktdeling"</string>
- <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Deling av Internett-tilkobling"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Deling av internettilkobling"</string>
<string name="bluetooth_profile_map" msgid="1019763341565580450">"Tekstmeldinger"</string>
<string name="bluetooth_profile_sap" msgid="5764222021851283125">"Tilgang til SIM-kortet"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-lyd: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
@@ -90,7 +90,7 @@
<string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Ikke koblet til tjener for filoverføring"</string>
<string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Koblet til inndataenhet"</string>
<string name="bluetooth_pan_user_profile_summary_connected" msgid="6436258151814414028">"Koblet til enhet for Internett-tilgang"</string>
- <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"Deler lokal Internett-tilkobling med enhet"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"Deler lokal internettilkobling med enhet"</string>
<string name="bluetooth_pan_profile_summary_use_for" msgid="5736111170225304239">"Bruk for Internett-tilgang"</string>
<string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Bruk for kart"</string>
<string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Bruk for tilgang til SIM-kortet"</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 424368d..b071355 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -418,7 +418,7 @@
case Settings.CALL_METHOD_PUT_CONFIG: {
String value = getSettingValue(args);
final boolean makeDefault = getSettingMakeDefault(args);
- insertConfigSetting(name, value, null, makeDefault, requestingUserId, false);
+ insertConfigSetting(name, value, makeDefault);
break;
}
@@ -447,7 +447,7 @@
case Settings.CALL_METHOD_RESET_CONFIG: {
final int mode = getResetModeEnforcingPermission(args);
String prefix = getSettingPrefix(args);
- resetConfigSetting(requestingUserId, mode, prefix);
+ resetConfigSetting(mode, prefix);
break;
}
@@ -466,7 +466,7 @@
}
case Settings.CALL_METHOD_DELETE_CONFIG: {
- int rows = deleteConfigSetting(name, requestingUserId, false) ? 1 : 0;
+ int rows = deleteConfigSetting(name) ? 1 : 0;
Bundle result = new Bundle();
result.putInt(RESULT_ROWS_DELETED, rows);
return result;
@@ -1067,38 +1067,33 @@
}
}
- private boolean insertConfigSetting(String name, String value, String tag,
- boolean makeDefault, int requestingUserId, boolean forceNotify) {
+ private boolean insertConfigSetting(String name, String value, boolean makeDefault) {
if (DEBUG) {
Slog.v(LOG_TAG, "insertConfigSetting(" + name + ", " + value + ", "
- + ", " + tag + ", " + makeDefault + ", " + requestingUserId
- + ", " + forceNotify + ")");
+ + makeDefault + ")");
}
- return mutateConfigSetting(name, value, tag, makeDefault, requestingUserId,
- MUTATION_OPERATION_INSERT, forceNotify, 0);
+ return mutateConfigSetting(name, value, null, makeDefault,
+ MUTATION_OPERATION_INSERT, 0);
}
- private boolean deleteConfigSetting(String name, int requestingUserId, boolean forceNotify) {
+ private boolean deleteConfigSetting(String name) {
if (DEBUG) {
- Slog.v(LOG_TAG, "deleteConfigSetting(" + name + ", " + requestingUserId
- + ", " + forceNotify + ")");
+ Slog.v(LOG_TAG, "deleteConfigSetting(" + name + ")");
}
- return mutateConfigSetting(name, null, null, false, requestingUserId,
- MUTATION_OPERATION_DELETE, forceNotify, 0);
+ return mutateConfigSetting(name, null, null, false,
+ MUTATION_OPERATION_DELETE, 0);
}
- private void resetConfigSetting(int requestingUserId, int mode, String prefix) {
+ private void resetConfigSetting(int mode, String prefix) {
if (DEBUG) {
- Slog.v(LOG_TAG, "resetConfigSetting(" + requestingUserId + ", "
- + mode + ", " + prefix + ")");
+ Slog.v(LOG_TAG, "resetConfigSetting(" + mode + ", " + prefix + ")");
}
- mutateConfigSetting(null, null, prefix, false, requestingUserId,
- MUTATION_OPERATION_RESET, false, mode);
+ mutateConfigSetting(null, null, prefix, false,
+ MUTATION_OPERATION_RESET, mode);
}
private boolean mutateConfigSetting(String name, String value, String prefix,
- boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
- int mode) {
+ boolean makeDefault, int operation, int mode) {
// TODO(b/117663715): check the new permission when it's added.
// enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
@@ -1107,13 +1102,13 @@
switch (operation) {
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
- UserHandle.USER_SYSTEM, name, value, null, makeDefault,
- getCallingPackage(), forceNotify, null);
+ UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
+ getCallingPackage(), false, null);
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
- UserHandle.USER_SYSTEM, name, forceNotify, null);
+ UserHandle.USER_SYSTEM, name, false, null);
}
case MUTATION_OPERATION_RESET: {
@@ -2631,13 +2626,20 @@
public boolean insertSettingLocked(int type, int userId, String name, String value,
String tag, boolean makeDefault, String packageName, boolean forceNotify,
Set<String> criticalSettings) {
+ return insertSettingLocked(type, userId, name, value, tag, makeDefault, false,
+ packageName, forceNotify, criticalSettings);
+ }
+
+ public boolean insertSettingLocked(int type, int userId, String name, String value,
+ String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName,
+ boolean forceNotify, Set<String> criticalSettings) {
final int key = makeKey(type, userId);
boolean success = false;
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState != null) {
success = settingsState.insertSettingLocked(name, value,
- tag, makeDefault, packageName);
+ tag, makeDefault, forceNonSystemPackage, packageName);
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index ae2ca3f..521163f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -377,6 +377,13 @@
@GuardedBy("mLock")
public boolean insertSettingLocked(String name, String value, String tag,
boolean makeDefault, String packageName) {
+ return insertSettingLocked(name, value, tag, makeDefault, false, packageName);
+ }
+
+ // The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
+ public boolean insertSettingLocked(String name, String value, String tag,
+ boolean makeDefault, boolean forceNonSystemPackage, String packageName) {
if (TextUtils.isEmpty(name)) {
return false;
}
@@ -387,7 +394,7 @@
Setting newState;
if (oldState != null) {
- if (!oldState.update(value, makeDefault, packageName, tag, false)) {
+ if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage)) {
return false;
}
newState = oldState;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
index 59de6a7e..9d0462e 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
@@ -17,10 +17,9 @@
package com.android.providers.settings;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
-import static org.junit.Assert.assertNotNull;
-
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Bundle;
@@ -147,10 +146,10 @@
}
@Test
- public void testReset_setUntrustedDefault() throws Exception {
+ public void testReset() throws Exception {
String newValue = "value2";
- // make sValue the untrusted default (set by root)
+ // make sValue the default value
executeShellCommand(
"device_config put " + sNamespace + " " + sKey + " " + sValue + " default");
// make newValue the current value
@@ -159,40 +158,19 @@
String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
assertEquals(newValue, result);
+ // reset values that were set by untrusted packages
executeShellCommand("device_config reset untrusted_defaults " + sNamespace);
result = getFromContentProvider(mContentResolver, sNamespace, sKey);
- // back to the default
+ // the default value has been restored
assertEquals(sValue, result);
+ // clear values that were set by untrusted packages
executeShellCommand("device_config reset trusted_defaults " + sNamespace);
result = getFromContentProvider(mContentResolver, sNamespace, sKey);
- // not trusted default was set
+ // even the default value is gone now
assertNull(result);
}
- @Test
- public void testReset_setTrustedDefault() throws Exception {
- String newValue = "value2";
-
- // make sValue the trusted default (set by system)
- putWithContentProvider(mContentResolver, sNamespace, sKey, sValue, true);
- // make newValue the current value
- executeShellCommand(
- "device_config put " + sNamespace + " " + sKey + " " + newValue);
- String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
- assertEquals(newValue, result);
-
- executeShellCommand("device_config reset untrusted_defaults " + sNamespace);
- result = getFromContentProvider(mContentResolver, sNamespace, sKey);
- // back to the default
- assertEquals(sValue, result);
-
- executeShellCommand("device_config reset trusted_defaults " + sNamespace);
- result = getFromContentProvider(mContentResolver, sNamespace, sKey);
- // our trusted default is still set
- assertEquals(sValue, result);
- }
-
private static void executeShellCommand(String command) throws IOException {
InputStream is = new FileInputStream(InstrumentationRegistry.getInstrumentation()
.getUiAutomation().executeShellCommand(command).getFileDescriptor());
@@ -212,8 +190,7 @@
if (makeDefault) {
args.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
- resolver.call(
- CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, compositeName, args);
+ resolver.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, compositeName, args);
}
private static String getFromContentProvider(ContentResolver resolver, String namespace,
diff --git a/packages/Shell/res/values-mr/strings.xml b/packages/Shell/res/values-mr/strings.xml
index dc6a6126..9595e28 100644
--- a/packages/Shell/res/values-mr/strings.xml
+++ b/packages/Shell/res/values-mr/strings.xml
@@ -28,7 +28,7 @@
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तुमचा बग रीपोर्ट स्क्रीनशॉटशिवाय शेअर करण्यासाठी टॅप करा किंवा स्क्रीनशॉट पूर्ण होण्याची प्रतीक्षा करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
- <string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
+ <string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"पुन्हा दर्शवू नका"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रीपोर्ट"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रीपोर्ट फाईल वाचणे शक्य झाले नाही"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 7ea4027..6fb44cc 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -89,7 +89,7 @@
<string name="usb_preference_title" msgid="6551050377388882787">"USB फाईल स्थानांतरण पर्याय"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"मीडिया प्लेअर म्हणून माउंट करा (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"कॅमेरा म्हणून माउंट करा (PTP)"</string>
- <string name="installer_cd_button_title" msgid="2312667578562201583">"Mac साठी Android फाईल स्थानांतर अॅप इंस्टॉल करा"</string>
+ <string name="installer_cd_button_title" msgid="2312667578562201583">"Mac साठी Android फाईल स्थानांतर अॅप इंस्टॉल करा"</string>
<string name="accessibility_back" msgid="567011538994429120">"मागे"</string>
<string name="accessibility_home" msgid="8217216074895377641">"होम"</string>
<string name="accessibility_menu" msgid="316839303324695949">"मेनू"</string>
@@ -598,7 +598,7 @@
<string name="tuner_full_importance_settings" msgid="3207312268609236827">"पॉवर सूचना नियंत्रणे"</string>
<string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"चालू"</string>
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"बंद"</string>
- <string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- फुल स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n\n"<b>"स्तर 1"</b>\n"- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना ब्लॉक करा"</string>
+ <string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- फुल स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n\n"<b>"स्तर 1"</b>\n"- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना ब्लॉक करा"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"आता तुम्हाला या सूचना दिसणार नाहीत"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"या सूचना लहान केल्या जातील"</string>
@@ -621,13 +621,13 @@
<string name="inline_keep_showing_app" msgid="1723113469580031041">"या अॅपकडील सूचना दाखवणे सुरू ठेवायचे?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"या सूचना बंद करता येत नाहीत"</string>
<string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> मार्गे"</string>
- <string name="appops_camera" msgid="8100147441602585776">"हे अॅप कॅमेरा वापरत आहे."</string>
- <string name="appops_microphone" msgid="741508267659494555">"हे अॅप मायक्रोफोन वापरत आहे."</string>
- <string name="appops_overlay" msgid="6165912637560323464">"हे अॅप स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे."</string>
- <string name="appops_camera_mic" msgid="1576901651150187433">"हे अॅप मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
- <string name="appops_camera_overlay" msgid="8869400080809298814">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि कॅमेरा वापरत आहे."</string>
- <string name="appops_mic_overlay" msgid="4835157962857919804">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन वापरत आहे."</string>
- <string name="appops_camera_mic_overlay" msgid="6718768197048030993">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
+ <string name="appops_camera" msgid="8100147441602585776">"हे अॅप कॅमेरा वापरत आहे."</string>
+ <string name="appops_microphone" msgid="741508267659494555">"हे अॅप मायक्रोफोन वापरत आहे."</string>
+ <string name="appops_overlay" msgid="6165912637560323464">"हे अॅप स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे."</string>
+ <string name="appops_camera_mic" msgid="1576901651150187433">"हे अॅप मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
+ <string name="appops_camera_overlay" msgid="8869400080809298814">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि कॅमेरा वापरत आहे."</string>
+ <string name="appops_mic_overlay" msgid="4835157962857919804">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन वापरत आहे."</string>
+ <string name="appops_camera_mic_overlay" msgid="6718768197048030993">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
<string name="notification_appops_settings" msgid="1028328314935908050">"सेटिंग्ज"</string>
<string name="notification_appops_ok" msgid="1156966426011011434">"ओके"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे खुली आहेत"</string>
@@ -776,8 +776,8 @@
<string name="accessibility_qs_edit_tile_move" msgid="3108103090006972938">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> स्थानावर हलवा"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"द्रुत सेटिंग्ज संपादक."</string>
<string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
- <string name="dock_forced_resizable" msgid="5914261505436217520">"अॅप कदाचित विभाजित-स्क्रीनसह कार्य करू शकत नाही."</string>
- <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"अॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
+ <string name="dock_forced_resizable" msgid="5914261505436217520">"अॅप कदाचित विभाजित-स्क्रीनसह कार्य करू शकत नाही."</string>
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"अॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
<string name="forced_resizable_secondary_display" msgid="4230857851756391925">"दुसऱ्या डिस्प्लेवर अॅप कदाचित चालणार नाही."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="7793821742158306742">"दुसऱ्या डिस्प्लेवर अॅप लाँच होणार नाही."</string>
<string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"सेटिंग्ज उघडा."</string>
@@ -806,7 +806,7 @@
<string name="pip_skip_to_prev" msgid="1955311326688637914">"डावलून मागे जा"</string>
<string name="thermal_shutdown_title" msgid="4458304833443861111">"तापल्यामुळे फोन बंद झाला"</string>
<string name="thermal_shutdown_message" msgid="9006456746902370523">"तुमचा फोन आता व्यवस्थित चालू आहे"</string>
- <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"तुमचा फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. तुमचा फोन आता व्यवस्थित चालू आहे.\n\nतुम्ही असे केल्यास तुमचा फोन खूप तापेल:\n •संसाधन केंद्रित अॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अॅप यासारखे)\n •मोठ्या फायली डाउनलोड किंवा अपलोड करणे\n •उच्च तापमानामध्ये तुमचा फोन वापरणे"</string>
+ <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"तुमचा फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. तुमचा फोन आता व्यवस्थित चालू आहे.\n\nतुम्ही असे केल्यास तुमचा फोन खूप तापेल:\n •संसाधन केंद्रित अॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अॅप यासारखे)\n •मोठ्या फायली डाउनलोड किंवा अपलोड करणे\n •उच्च तापमानामध्ये तुमचा फोन वापरणे"</string>
<string name="high_temp_title" msgid="4589508026407318374">"फोन ऊष्ण होत आहे"</string>
<string name="high_temp_notif_message" msgid="5642466103153429279">"फोन थंड होत असताना काही वैशिष्ट्ये मर्यादित असतात"</string>
<string name="high_temp_dialog_message" msgid="6840700639374113553">"तुमचा फोन स्वयंचलितपणे थंड होईल. तुम्ही अद्यापही तुमचा फोन वापरू शकता परंतु तो कदाचित धीमेपणे कार्य करेल.\n\nतुमचा फोन एकदा थंड झाला की, तो सामान्यपणे कार्य करेल."</string>
@@ -832,9 +832,9 @@
<string name="notification_channel_hints" msgid="7323870212489152689">"सूचना"</string>
<string name="instant_apps" msgid="6647570248119804907">"इन्सटंट अॅप्स"</string>
<string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> रन होत आहे"</string>
- <string name="instant_apps_message" msgid="1183313016396018086">"इंस्टॉल केल्याशिवाय अॅप उघडले."</string>
- <string name="instant_apps_message_with_help" msgid="6179830437630729747">"इंस्टॉल केल्याशिवाय अॅप उघडले. अधिक जाणून घेण्यासाठी टॅप करा."</string>
- <string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"इंस्टॉल केल्याशिवाय अॅप उघडले."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"इंस्टॉल केल्याशिवाय अॅप उघडले. अधिक जाणून घेण्यासाठी टॅप करा."</string>
+ <string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
<string name="go_to_web" msgid="2650669128861626071">"ब्राउझरवर जा"</string>
<string name="mobile_data" msgid="7094582042819250762">"मोबाइल डेटा"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
@@ -873,7 +873,7 @@
<!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"रद्द करा"</string>
<string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"तपशील पाहा"</string>
- <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"अॅप तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"अॅप तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे"</string>
<string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"अॅप्स तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहेत"</string>
<string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" आणि "</string>
@@ -882,6 +882,6 @@
<string name="privacy_type_microphone" msgid="4153045784928554506">"मायक्रोफोन"</string>
<plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
<item quantity="one">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अॅप</item>
- <item quantity="other">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अॅप्स</item>
+ <item quantity="other">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अॅप्स</item>
</plurals>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 4f5b23e..558fd9b 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -785,7 +785,7 @@
<string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"Lukk hurtiginnstillingene."</string>
<string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Alarm er angitt."</string>
<string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Logget på som <xliff:g id="ID_1">%s</xliff:g>"</string>
- <string name="data_connection_no_internet" msgid="4503302451650972989">"Ingen Internett-tilkobling"</string>
+ <string name="data_connection_no_internet" msgid="4503302451650972989">"Ingen internettilkobling"</string>
<string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Åpne informasjonen."</string>
<string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Utilgjengelig fordi <xliff:g id="REASON">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Åpne <xliff:g id="ID_1">%s</xliff:g>-innstillingene."</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 5e7a119..06467b8 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -651,7 +651,7 @@
<item quantity="other">%d minuten</item>
<item quantity="one">%d minuut</item>
</plurals>
- <string name="battery_panel_title" msgid="7944156115535366613">"Accugebruik"</string>
+ <string name="battery_panel_title" msgid="7944156115535366613">"Batterijgebruik"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterijbesparing niet beschikbaar tijdens opladen"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterijbesparing"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Vermindert de prestaties en achtergrondgegevens"</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
index a9cf857..807edf6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
@@ -25,6 +25,8 @@
import android.view.View;
import android.view.ViewRootImpl;
+import java.util.function.Consumer;
+
/**
* Helper class to apply surface transactions in sync with RenderThread.
*/
@@ -78,6 +80,35 @@
applyParams(t.mTransaction, params, t.mTmpValues);
}
+ /**
+ * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
+ * attached if necessary.
+ */
+ public static void create(final View targetView,
+ final Consumer<SyncRtSurfaceTransactionApplier> callback) {
+ if (targetView == null) {
+ // No target view, no applier
+ callback.accept(null);
+ } else if (targetView.getViewRootImpl() != null) {
+ // Already attached, we're good to go
+ callback.accept(new SyncRtSurfaceTransactionApplier(targetView));
+ } else {
+ // Haven't been attached before we can get the view root
+ targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ targetView.removeOnAttachStateChangeListener(this);
+ callback.accept(new SyncRtSurfaceTransactionApplier(targetView));
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ // Do nothing
+ }
+ });
+ }
+ }
+
private static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
t.setMatrix(params.surface, params.matrix, tmpFloat9);
t.setWindowCrop(params.surface, params.windowCrop);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index ba69f3b..9391737 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -218,88 +218,7 @@
return false;
}
- ViewParent p = view.getParent();
- RemoteInputView riv = null;
- while (p != null) {
- if (p instanceof View) {
- View pv = (View) p;
- if (pv.isRootNamespace()) {
- riv = findRemoteInputView(pv);
- break;
- }
- }
- p = p.getParent();
- }
- ExpandableNotificationRow row = null;
- while (p != null) {
- if (p instanceof ExpandableNotificationRow) {
- row = (ExpandableNotificationRow) p;
- break;
- }
- p = p.getParent();
- }
-
- if (row == null) {
- return false;
- }
-
- row.setUserExpanded(true);
-
- if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
- final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
- if (mLockscreenUserManager.isLockscreenPublicMode(userId)) {
- mCallback.onLockedRemoteInput(row, view);
- return true;
- }
- if (mUserManager.getUserInfo(userId).isManagedProfile()
- && mKeyguardManager.isDeviceLocked(userId)) {
- mCallback.onLockedWorkRemoteInput(userId, row, view);
- return true;
- }
- }
-
- if (riv == null) {
- riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild());
- if (riv == null) {
- return false;
- }
- if (!row.getPrivateLayout().getExpandedChild().isShown()) {
- mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
- return true;
- }
- }
-
- int width = view.getWidth();
- if (view instanceof TextView) {
- // Center the reveal on the text which might be off-center from the TextView
- TextView tv = (TextView) view;
- if (tv.getLayout() != null) {
- int innerWidth = (int) tv.getLayout().getLineWidth(0);
- innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
- width = Math.min(width, innerWidth);
- }
- }
- int cx = view.getLeft() + width / 2;
- int cy = view.getTop() + view.getHeight() / 2;
- int w = riv.getWidth();
- int h = riv.getHeight();
- int r = Math.max(
- Math.max(cx + cy, cx + (h - cy)),
- Math.max((w - cx) + cy, (w - cx) + (h - cy)));
-
- riv.setRevealParameters(cx, cy, r);
- riv.setPendingIntent(pendingIntent);
- riv.setRemoteInput(inputs, input);
- riv.focusAnimated();
-
- return true;
- }
-
- private RemoteInputView findRemoteInputView(View v) {
- if (v == null) {
- return null;
- }
- return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
+ return activateRemoteInput(view, inputs, input, pendingIntent);
}
};
@@ -355,6 +274,102 @@
}
/**
+ * Activates a given {@link RemoteInput}
+ *
+ * @param view The view of the action button or suggestion chip that was tapped.
+ * @param inputs The remote inputs that need to be sent to the app.
+ * @param input The remote input that needs to be activated.
+ * @param pendingIntent The pending intent to be sent to the app.
+ * @return Whether the {@link RemoteInput} was activated.
+ */
+ public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
+ PendingIntent pendingIntent) {
+
+ ViewParent p = view.getParent();
+ RemoteInputView riv = null;
+ while (p != null) {
+ if (p instanceof View) {
+ View pv = (View) p;
+ if (pv.isRootNamespace()) {
+ riv = findRemoteInputView(pv);
+ break;
+ }
+ }
+ p = p.getParent();
+ }
+ ExpandableNotificationRow row = null;
+ while (p != null) {
+ if (p instanceof ExpandableNotificationRow) {
+ row = (ExpandableNotificationRow) p;
+ break;
+ }
+ p = p.getParent();
+ }
+
+ if (row == null) {
+ return false;
+ }
+
+ row.setUserExpanded(true);
+
+ if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
+ final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+ if (mLockscreenUserManager.isLockscreenPublicMode(userId)) {
+ mCallback.onLockedRemoteInput(row, view);
+ return true;
+ }
+ if (mUserManager.getUserInfo(userId).isManagedProfile()
+ && mKeyguardManager.isDeviceLocked(userId)) {
+ mCallback.onLockedWorkRemoteInput(userId, row, view);
+ return true;
+ }
+ }
+
+ if (riv == null) {
+ riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild());
+ if (riv == null) {
+ return false;
+ }
+ if (!row.getPrivateLayout().getExpandedChild().isShown()) {
+ mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
+ return true;
+ }
+ }
+
+ int width = view.getWidth();
+ if (view instanceof TextView) {
+ // Center the reveal on the text which might be off-center from the TextView
+ TextView tv = (TextView) view;
+ if (tv.getLayout() != null) {
+ int innerWidth = (int) tv.getLayout().getLineWidth(0);
+ innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
+ width = Math.min(width, innerWidth);
+ }
+ }
+ int cx = view.getLeft() + width / 2;
+ int cy = view.getTop() + view.getHeight() / 2;
+ int w = riv.getWidth();
+ int h = riv.getHeight();
+ int r = Math.max(
+ Math.max(cx + cy, cx + (h - cy)),
+ Math.max((w - cx) + cy, (w - cx) + (h - cy)));
+
+ riv.setRevealParameters(cx, cy, r);
+ riv.setPendingIntent(pendingIntent);
+ riv.setRemoteInput(inputs, input);
+ riv.focusAnimated();
+
+ return true;
+ }
+
+ private RemoteInputView findRemoteInputView(View v) {
+ if (v == null) {
+ return null;
+ }
+ return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
+ }
+
+ /**
* Adds all the notification lifetime extenders. Each extender represents a reason for the
* NotificationRemoteInputManager to keep a notification lifetime extended.
*/
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 d9fe982..f543b46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -588,7 +588,7 @@
}
public boolean areGutsExposed() {
- return row != null && row.getGuts().isExposed();
+ return row != null && row.getGuts() != null && row.getGuts().isExposed();
}
public boolean isChildInGroup() {
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 8214ea6..8bed366 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
@@ -1098,6 +1098,10 @@
mHeadsUpManager = headsUpManager;
}
+ public HeadsUpManager getHeadsUpManager() {
+ return mHeadsUpManager;
+ }
+
public void setGutsView(MenuItem item) {
if (mGuts != null && item.getGutsView() instanceof NotificationGuts.GutsContent) {
((NotificationGuts.GutsContent) item.getGutsView()).setGutsParent(mGuts);
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 689d6d5..edd54ca 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
@@ -91,6 +91,7 @@
private SmartReplyConstants mSmartReplyConstants;
private SmartReplyView mExpandedSmartReplyView;
+ private SmartReplyView mHeadsUpSmartReplyView;
private SmartReplyController mSmartReplyController;
private NotificationViewWrapper mContractedWrapper;
@@ -253,6 +254,9 @@
}
if (mHeadsUpChild != null) {
int maxHeight = mHeadsUpHeight;
+ if (mHeadsUpSmartReplyView != null) {
+ maxHeight += mHeadsUpSmartReplyView.getHeightUpperLimit();
+ }
maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
int size = maxHeight;
ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
@@ -955,6 +959,9 @@
if (mExpandedSmartReplyView != null) {
mExpandedSmartReplyView.setBackgroundTintColor(color);
}
+ if (mHeadsUpSmartReplyView != null) {
+ mHeadsUpSmartReplyView.setBackgroundTintColor(color);
+ }
}
public int getVisibleType() {
@@ -1472,6 +1479,10 @@
entry, smartRepliesAndActions.smartReplies.choices.length);
}
}
+ if (mHeadsUpChild != null) {
+ mHeadsUpSmartReplyView =
+ applySmartReplyView(mHeadsUpChild, smartRepliesAndActions, entry);
+ }
}
private SmartReplyView applySmartReplyView(View view,
@@ -1520,7 +1531,8 @@
}
if (smartRepliesAndActions.smartActions != null) {
smartReplyView.addSmartActions(
- smartRepliesAndActions.smartActions, mSmartReplyController, entry);
+ smartRepliesAndActions.smartActions, mSmartReplyController, entry,
+ mContainingNotification.getHeadsUpManager());
}
smartReplyContainer.setVisibility(View.VISIBLE);
}
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 9eb5737a..0cec637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -296,13 +296,15 @@
} else if (exceededSwipeHorizontalTouchSlop) {
if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) {
// Swiping left (rtl) gesture
- int index = isEdgeSwipeAlongNavBar(touchDownH, !mDragHPositive)
+ int index = mGestureActions[ACTION_SWIPE_LEFT_FROM_EDGE_INDEX] != null
+ && isEdgeSwipeAlongNavBar(touchDownH, !mDragHPositive)
? ACTION_SWIPE_LEFT_FROM_EDGE_INDEX : ACTION_SWIPE_LEFT_INDEX;
tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */,
event);
} else {
// Swiping right (ltr) gesture
- int index = isEdgeSwipeAlongNavBar(touchDownH, mDragHPositive)
+ int index = mGestureActions[ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX] != null
+ && isEdgeSwipeAlongNavBar(touchDownH, mDragHPositive)
? ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX : ACTION_SWIPE_RIGHT_INDEX;
tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */,
event);
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 b351f1d..1d6a1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -121,8 +121,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.widget.MessagingGroup;
-import com.android.internal.widget.MessagingMessage;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
@@ -1116,8 +1114,6 @@
@Override
public void onDensityOrFontScaleChanged() {
- MessagingMessage.dropCache();
- MessagingGroup.dropCache();
// TODO: Remove this.
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onDensityOrFontScaleChanged();
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 d99af1f..acdd5e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -37,6 +37,8 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.widget.MessagingGroup;
+import com.android.internal.widget.MessagingMessage;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
@@ -62,9 +64,11 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
-public class StatusBarNotificationPresenter implements NotificationPresenter {
+public class StatusBarNotificationPresenter implements NotificationPresenter,
+ ConfigurationController.ConfigurationListener {
private final LockscreenGestureLogger mLockscreenGestureLogger =
Dependency.get(LockscreenGestureLogger.class);
@@ -168,9 +172,13 @@
onUserSwitched(mLockscreenUserManager.getCurrentUserId());
});
+ Dependency.get(ConfigurationController.class).addCallback(this);
}
+ @Override
public void onDensityOrFontScaleChanged() {
+ MessagingMessage.dropCache();
+ MessagingGroup.dropCache();
if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
} else {
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 2a4336e..4fa8321 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -16,6 +16,8 @@
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.text.Layout;
import android.text.TextPaint;
import android.text.method.TransformationMethod;
@@ -34,6 +36,7 @@
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -61,6 +64,8 @@
private final SmartReplyConstants mConstants;
private final KeyguardDismissUtil mKeyguardDismissUtil;
+ private final NotificationRemoteInputManager mRemoteInputManager;
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
/**
* The upper bound for the height of this view in pixels. Notifications are automatically
@@ -109,6 +114,7 @@
super(context, attrs);
mConstants = Dependency.get(SmartReplyConstants.class);
mKeyguardDismissUtil = Dependency.get(KeyguardDismissUtil.class);
+ mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
mHeightUpperLimit = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.smart_reply_button_max_height);
@@ -209,13 +215,15 @@
* notification are shown.
*/
public void addSmartActions(SmartActions smartActions,
- SmartReplyController smartReplyController, NotificationData.Entry entry) {
+ SmartReplyController smartReplyController, NotificationData.Entry entry,
+ HeadsUpManager headsUpManager) {
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);
+ getContext(), this, n, smartActions, smartReplyController, entry,
+ headsUpManager);
addView(actionButton);
}
}
@@ -237,12 +245,22 @@
b.setText(choice);
OnDismissAction action = () -> {
+ // TODO(b/111437455): Also for EDIT_CHOICES_BEFORE_SENDING_AUTO, depending on flags.
+ if (smartReplies.remoteInput.getEditChoicesBeforeSending()
+ == RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED) {
+ entry.remoteInputText = choice;
+ mRemoteInputManager.activateRemoteInput(b,
+ new RemoteInput[] { smartReplies.remoteInput }, smartReplies.remoteInput,
+ smartReplies.pendingIntent);
+ return false;
+ }
+
smartReplyController.smartReplySent(
entry, replyIndex, b.getText(), smartReplies.fromAssistant);
Bundle results = new Bundle();
results.putString(smartReplies.remoteInput.getResultKey(), choice.toString());
Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addResultsToIntent(new RemoteInput[]{smartReplies.remoteInput}, intent,
+ RemoteInput.addResultsToIntent(new RemoteInput[] { smartReplies.remoteInput }, intent,
results);
RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE);
entry.setHasSentReply();
@@ -274,7 +292,7 @@
@VisibleForTesting
Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
SmartActions smartActions, SmartReplyController smartReplyController,
- NotificationData.Entry entry) {
+ NotificationData.Entry entry, HeadsUpManager headsUpManager) {
Notification.Action action = smartActions.actions.get(actionIndex);
Button button = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_action_button, root, false);
@@ -290,8 +308,12 @@
button.setOnClickListener(view ->
getActivityStarter().startPendingIntentDismissingKeyguard(
action.actionIntent,
- () -> smartReplyController.smartActionClicked(
- entry, actionIndex, action, smartActions.fromAssistant)));
+ () -> {
+ smartReplyController.smartActionClicked(
+ entry, actionIndex, action, smartActions.fromAssistant);
+ postOnUiThread(() ->
+ headsUpManager.removeNotification(entry.key, true));
+ }));
// TODO(b/119010281): handle accessibility
@@ -301,6 +323,10 @@
return button;
}
+ private void postOnUiThread(Runnable runnable) {
+ mMainThreadHandler.post(runnable);
+ }
+
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(mContext, attrs);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
index abb8c79..2805908 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
@@ -601,6 +601,25 @@
assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_LEFT);
}
+ @Test
+ public void testNoEdgeActionsTriggerNormalActions() {
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
+
+ NavigationGestureAction swipeUp = mockAction(true);
+ NavigationGestureAction swipeDown = mockAction(true);
+ NavigationGestureAction swipeLeft = mockAction(true);
+ NavigationGestureAction swipeRight = mockAction(true);
+ mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
+ null /* swipeLeftFromEdgeAction */,
+ null /* swipeLeftFromEdgeAction */);
+
+ // Swipe Left from Edge
+ assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH, 1, 5, 1);
+ // Swipe Right from Edge
+ assertGestureTriggersAction(swipeRight, 0, 1, NAVBAR_WIDTH, 5);
+ }
+
private void assertGestureDragsHitTargetAllDirections(View buttonView, boolean isRTL,
int navPos) {
mController.setBarState(isRTL, navPos);
@@ -665,7 +684,6 @@
reset(buttonView);
}
-
private MotionEvent event(int action, float x, float y) {
final MotionEvent event = mock(MotionEvent.class);
doReturn(x).when(event).getX();
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 506fa97..c5bac92 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
@@ -98,6 +98,7 @@
private Notification mNotification;
@Mock ActivityStarter mActivityStarter;
+ @Mock HeadsUpManager mHeadsUpManager;
@Before
public void setUp() {
@@ -434,7 +435,8 @@
mView.addSmartActions(
new SmartReplyView.SmartActions(createActions(actionTitles), false),
mLogger,
- mEntry);
+ mEntry,
+ mHeadsUpManager);
}
private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
@@ -442,7 +444,8 @@
mView.addSmartActions(
new SmartReplyView.SmartActions(createActions(actionTitles), false),
mLogger,
- mEntry);
+ mEntry,
+ mHeadsUpManager);
}
private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
@@ -747,7 +750,7 @@
private Button inflateActionButton(Notification.Action action) {
return mView.inflateActionButton(getContext(), mView, 0,
new SmartReplyView.SmartActions(Collections.singletonList(action), false),
- mLogger, mEntry);
+ mLogger, mEntry, mHeadsUpManager);
}
@Test
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index f2ae161..2d30640 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6627,6 +6627,16 @@
// OS: Q
ACTION_DISPLAY_FOLD = 1594;
+ // OPEN: WifiDppConfiguratorActivity (android.settings.WIFI_DPP_CONFIGURATOR_XXX action intents)
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_WIFI_DPP_CONFIGURATOR = 1595;
+
+ // OPEN: WifiDppEnrolleeActivity (android.settings.WIFI_DPP_ENROLLEE_XXX action intents)
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_WIFI_DPP_ENROLLEE = 1596;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index fba639c..1212676 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -216,6 +216,10 @@
// Package: android
NOTE_SOFTAP_CONFIG_CHANGED = 50;
+ // Notify the user that connected to app suggested network.
+ // Package: android
+ NOTE_CONNECTED_TO_NETWORK_SUGGESTION = 51;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d5decce..36ca52a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2466,6 +2466,25 @@
}
/**
+ * AIDL-exposed method. System only.
+ * Gets accessibility window id from window token.
+ *
+ * @param windowToken Window token to get accessibility window id.
+ * @return Accessibility window id for the window token. Returns -1 if no such token is
+ * registered.
+ */
+ @Override
+ public int getAccessibilityWindowId(IBinder windowToken) {
+ synchronized (mLock) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
+ throw new SecurityException("Only SYSTEM can call getAccessibilityWindowId");
+ }
+
+ return findWindowIdLocked(windowToken);
+ }
+ }
+
+ /**
* Get the recommended timeout of interactive controls and non-interactive controls.
*
* @return A long for pair of {@code int}s. First integer for interactive one, and second
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 5a0d12c..fa62ef8 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -76,7 +76,6 @@
import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.infra.AbstractPerUserSystemService;
-import com.android.server.infra.AbstractRemoteService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import com.android.server.infra.SecureSettingsServiceNameResolver;
@@ -1031,8 +1030,7 @@
return null;
}
final ComponentName componentName = RemoteAugmentedAutofillService.getComponentName(
- getContext(), serviceName, mUserId,
- mAugmentedAutofillResolver.isTemporaryLocked());
+ serviceName, mUserId, mAugmentedAutofillResolver.isTemporaryLocked());
if (componentName == null) return null;
if (sVerbose) {
Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): " + componentName);
@@ -1041,8 +1039,7 @@
mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(),
componentName, mUserId, new RemoteAugmentedAutofillServiceCallbacks() {
@Override
- public void onServiceDied(
- AbstractRemoteService<? extends AbstractRemoteService<?>> service) {
+ public void onServiceDied(@NonNull RemoteAugmentedAutofillService service) {
// TODO(b/111330312): properly implement
Slog.w(TAG, "remote augmented autofill service died");
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 222888c..fc7265d 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -26,7 +26,6 @@
import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.IInterface;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.augmented.AugmentedAutofillService;
@@ -43,7 +42,8 @@
import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
final class RemoteAugmentedAutofillService
- extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService> {
+ extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService,
+ IAugmentedAutofillService> {
private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName();
@@ -51,20 +51,16 @@
private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
- private final RemoteAugmentedAutofillServiceCallbacks mCallbacks;
- private IAugmentedAutofillService mService;
-
RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
boolean bindInstantServiceAllowed, boolean verbose) {
super(context, AugmentedAutofillService.SERVICE_INTERFACE, serviceName, userId, callbacks,
bindInstantServiceAllowed, verbose);
- mCallbacks = callbacks;
}
@Nullable
- public static ComponentName getComponentName(@NonNull Context context,
- @NonNull String componentName, @UserIdInt int userId, boolean isTemporary) {
+ public static ComponentName getComponentName(@NonNull String componentName,
+ @UserIdInt int userId, boolean isTemporary) {
int flags = PackageManager.GET_META_DATA;
if (!isTemporary) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
@@ -88,9 +84,8 @@
}
@Override // from AbstractRemoteService
- protected IInterface getServiceInterface(IBinder service) {
- mService = IAugmentedAutofillService.Stub.asInterface(service);
- return mService;
+ protected IAugmentedAutofillService getServiceInterface(IBinder service) {
+ return IAugmentedAutofillService.Stub.asInterface(service);
}
@Override // from AbstractRemoteService
@@ -109,7 +104,6 @@
public void onRequestAutofillLocked(int sessionId, @NonNull IAutoFillManagerClient client,
int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
@Nullable AutofillValue focusedValue) {
- cancelScheduledUnbind();
scheduleRequest(new PendingAutofillRequest(this, sessionId, client, taskId,
activityComponent, focusedId, focusedValue));
}
@@ -118,12 +112,12 @@
* Called by {@link Session} when it's time to destroy all augmented autofill requests.
*/
public void onDestroyAutofillWindowsRequest(int sessionId) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingDestroyAutofillWindowsRequest(this, sessionId));
+ scheduleAsyncRequest((s) -> s.onDestroyFillWindowRequest(sessionId));
}
+ // TODO(b/111330312): inline into PendingAutofillRequest if it doesn't have any other subclass
private abstract static class MyPendingRequest
- extends PendingRequest<RemoteAugmentedAutofillService> {
+ extends PendingRequest<RemoteAugmentedAutofillService, IAugmentedAutofillService> {
protected final int mSessionId;
private MyPendingRequest(@NonNull RemoteAugmentedAutofillService service, int sessionId) {
@@ -196,38 +190,8 @@
}
- private static final class PendingDestroyAutofillWindowsRequest extends MyPendingRequest {
-
- protected PendingDestroyAutofillWindowsRequest(
- @NonNull RemoteAugmentedAutofillService service, @NonNull int sessionId) {
- super(service, sessionId);
- }
-
- @Override
- public void run() {
- final RemoteAugmentedAutofillService remoteService = getService();
- if (remoteService == null) return;
-
- try {
- remoteService.mService.onDestroyFillWindowRequest(mSessionId);
- } catch (RemoteException e) {
- Slog.w(TAG, "exception handling onDestroyAutofillWindowsRequest() for "
- + mSessionId + ": " + e);
- } finally {
- // Service is not calling back, so we finish right away.
- finish();
- }
- }
-
- @Override
- protected void onTimeout(RemoteAugmentedAutofillService remoteService) {
- // Should not happen because we called finish() on run(), although currently it might
- // be called if the service is destroyed while showing it.
- Slog.e(TAG, "timed out: " + this);
- }
- }
-
- public interface RemoteAugmentedAutofillServiceCallbacks extends VultureCallback {
+ public interface RemoteAugmentedAutofillServiceCallbacks
+ extends VultureCallback<RemoteAugmentedAutofillService> {
// NOTE: so far we don't need to notify the callback implementation (an inner class on
// AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
// callback interface is empty.
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 4b7d290..417ea9c 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -28,7 +28,6 @@
import android.content.IntentSender;
import android.os.IBinder;
import android.os.ICancellationSignal;
-import android.os.IInterface;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
import android.service.autofill.FillRequest;
@@ -42,15 +41,15 @@
import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
-final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<RemoteFillService> {
+final class RemoteFillService
+ extends AbstractSinglePendingRequestRemoteService<RemoteFillService, IAutoFillService> {
private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
private final FillServiceCallbacks mCallbacks;
- private IAutoFillService mAutoFillService;
- public interface FillServiceCallbacks extends VultureCallback {
+ public interface FillServiceCallbacks extends VultureCallback<RemoteFillService> {
void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
@NonNull String servicePackageName, int requestFlags);
void onFillRequestFailure(int requestId, @Nullable CharSequence message);
@@ -71,21 +70,20 @@
@Override // from AbstractRemoteService
protected void handleOnConnectedStateChanged(boolean state) {
- if (mAutoFillService == null) {
+ if (mService == null) {
Slog.w(mTag, "onConnectedStateChanged(): null service");
return;
}
try {
- mAutoFillService.onConnectedStateChanged(state);
+ mService.onConnectedStateChanged(state);
} catch (Exception e) {
Slog.w(mTag, "Exception calling onConnectedStateChanged(): " + e);
}
}
@Override // from AbstractRemoteService
- protected IInterface getServiceInterface(IBinder service) {
- mAutoFillService = IAutoFillService.Stub.asInterface(service);
- return mAutoFillService;
+ protected IAutoFillService getServiceInterface(IBinder service) {
+ return IAutoFillService.Stub.asInterface(service);
}
@Override // from AbstractRemoteService
@@ -127,17 +125,15 @@
}
public void onFillRequest(@NonNull FillRequest request) {
- cancelScheduledUnbind();
scheduleRequest(new PendingFillRequest(request, this));
}
public void onSaveRequest(@NonNull SaveRequest request) {
- cancelScheduledUnbind();
scheduleRequest(new PendingSaveRequest(request, this));
}
private boolean handleResponseCallbackCommon(
- @NonNull PendingRequest<RemoteFillService> pendingRequest) {
+ @NonNull PendingRequest<RemoteFillService, IAutoFillService> pendingRequest) {
if (isDestroyed()) return false;
if (mPendingRequest == pendingRequest) {
@@ -204,7 +200,8 @@
});
}
- private static final class PendingFillRequest extends PendingRequest<RemoteFillService> {
+ private static final class PendingFillRequest
+ extends PendingRequest<RemoteFillService, IAutoFillService> {
private final FillRequest mRequest;
private final IFillCallback mCallback;
private ICancellationSignal mCancellation;
@@ -282,7 +279,7 @@
if (remoteService != null) {
if (sVerbose) Slog.v(mTag, "calling onFillRequest() for id=" + mRequest.getId());
try {
- remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
+ remoteService.mService.onFillRequest(mRequest, mCallback);
} catch (RemoteException e) {
Slog.e(mTag, "Error calling on fill request", e);
@@ -310,7 +307,8 @@
}
}
- private static final class PendingSaveRequest extends PendingRequest<RemoteFillService> {
+ private static final class PendingSaveRequest
+ extends PendingRequest<RemoteFillService, IAutoFillService> {
private final SaveRequest mRequest;
private final ISaveCallback mCallback;
@@ -355,7 +353,7 @@
if (remoteService != null) {
if (sVerbose) Slog.v(mTag, "calling onSaveRequest()");
try {
- remoteService.mAutoFillService.onSaveRequest(mRequest, mCallback);
+ remoteService.mService.onSaveRequest(mRequest, mCallback);
} catch (RemoteException e) {
Slog.e(mTag, "Error calling on save request", e);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3a22231..0e9b407 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -98,7 +98,6 @@
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.infra.AbstractRemoteService;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -909,7 +908,7 @@
// VultureCallback
@Override
- public void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service) {
+ public void onServiceDied(@NonNull RemoteFillService service) {
Slog.w(TAG, "removing session because service died");
forceRemoveSelfLocked();
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
index 2302b7d..a4012d5 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
@@ -29,7 +29,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
-import com.android.server.infra.AbstractRemoteService;
import java.io.PrintWriter;
import java.util.List;
@@ -124,8 +123,8 @@
}
}
- @Override // from RemoteScreenObservationServiceCallbacks
- public void onServiceDied(AbstractRemoteService<?> service) {
+ @Override // from RemoteContentCaptureServiceCallbacks
+ public void onServiceDied(@NonNull RemoteContentCaptureService service) {
// TODO(b/111276913): implement (remove session from PerUserSession?)
if (mService.isDebug()) {
Slog.d(TAG, "onServiceDied() for " + mId);
@@ -135,17 +134,6 @@
}
}
- @Override // from RemoteScreenObservationServiceCallbacks
- public void onFailureOrTimeout(boolean timedOut) {
- // TODO(b/111276913): log metrics on whether timed out or not
- if (mService.isDebug()) {
- Slog.d(TAG, "onFailureOrTimeout(" + mId + "): timed out=" + timedOut);
- }
- synchronized (mLock) {
- removeSelfLocked(/* notifyRemoteService= */ false);
- }
- }
-
@GuardedBy("mLock")
public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("id: "); pw.print(mId); pw.println();
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 6a111f2..33b6c8d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -20,14 +20,11 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
-import android.os.IInterface;
-import android.os.RemoteException;
import android.service.contentcapture.ContentCaptureEventsRequest;
import android.service.contentcapture.IContentCaptureService;
import android.service.contentcapture.InteractionContext;
import android.service.contentcapture.SnapshotData;
import android.text.format.DateUtils;
-import android.util.Slog;
import android.view.contentcapture.ContentCaptureEvent;
import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
@@ -35,30 +32,24 @@
import java.util.List;
final class RemoteContentCaptureService
- extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService> {
-
- private static final String TAG = RemoteContentCaptureService.class.getSimpleName();
+ extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
+ IContentCaptureService> {
// TODO(b/117779333): changed it so it's permanentely bound
private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
- private final ContentCaptureServiceCallbacks mCallbacks;
- private IContentCaptureService mService;
-
RemoteContentCaptureService(Context context, String serviceInterface,
ComponentName componentName, int userId,
ContentCaptureServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
boolean verbose) {
super(context, serviceInterface, componentName, userId, callbacks,
bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
- mCallbacks = callbacks;
}
@Override // from RemoteService
- protected IInterface getServiceInterface(@NonNull IBinder service) {
- mService = IContentCaptureService.Stub.asInterface(service);
- return mService;
+ protected IContentCaptureService getServiceInterface(@NonNull IBinder service) {
+ return IContentCaptureService.Stub.asInterface(service);
}
// TODO(b/111276913): modify super class to allow permanent binding when value is 0 or negative
@@ -81,8 +72,7 @@
*/
public void onSessionLifecycleRequest(@Nullable InteractionContext context,
@NonNull String sessionId) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingSessionLifecycleRequest(this, context, sessionId));
+ scheduleAsyncRequest((s) -> s.onSessionLifecycle(context, sessionId));
}
/**
@@ -90,8 +80,8 @@
*/
public void onContentCaptureEventsRequest(@NonNull String sessionId,
@NonNull List<ContentCaptureEvent> events) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingOnContentCaptureEventsRequest(this, sessionId, events));
+ scheduleAsyncRequest((s) -> s.onContentCaptureEventsRequest(sessionId,
+ new ContentCaptureEventsRequest(events)));
}
/**
@@ -99,103 +89,13 @@
*/
public void onActivitySnapshotRequest(@NonNull String sessionId,
@NonNull SnapshotData snapshotData) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingOnActivitySnapshotRequest(this, sessionId, snapshotData));
+ scheduleAsyncRequest((s) -> s.onActivitySnapshot(sessionId, snapshotData));
}
- private abstract static class MyPendingRequest
- extends PendingRequest<RemoteContentCaptureService> {
- protected final String mSessionId;
-
- private MyPendingRequest(@NonNull RemoteContentCaptureService service,
- @NonNull String sessionId) {
- super(service);
- mSessionId = sessionId;
- }
-
- @Override // from PendingRequest
- protected final void onTimeout(RemoteContentCaptureService remoteService) {
- Slog.w(TAG, "timed out handling " + getClass().getSimpleName() + " for "
- + mSessionId);
- remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ true);
- }
-
- @Override // from PendingRequest
- public final void run() {
- final RemoteContentCaptureService remoteService = getService();
- if (remoteService != null) {
- try {
- // 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 "
- + mSessionId + ": " + e);
- remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ false);
- }
- }
- }
-
- protected abstract void myRun(@NonNull RemoteContentCaptureService service)
- throws RemoteException;
-
- }
-
- private static final class PendingSessionLifecycleRequest extends MyPendingRequest {
-
- private final InteractionContext mContext;
-
- protected PendingSessionLifecycleRequest(@NonNull RemoteContentCaptureService service,
- @Nullable InteractionContext context, @NonNull String sessionId) {
- super(service, sessionId);
- mContext = context;
- }
-
- @Override // from MyPendingRequest
- public void myRun(@NonNull RemoteContentCaptureService remoteService)
- throws RemoteException {
- remoteService.mService.onSessionLifecycle(mContext, mSessionId);
- }
- }
-
- private static final class PendingOnContentCaptureEventsRequest extends MyPendingRequest {
-
- private final List<ContentCaptureEvent> mEvents;
-
- protected PendingOnContentCaptureEventsRequest(@NonNull RemoteContentCaptureService service,
- @NonNull String sessionId, @NonNull List<ContentCaptureEvent> events) {
- super(service, sessionId);
- mEvents = events;
- }
-
- @Override // from MyPendingRequest
- public void myRun(@NonNull RemoteContentCaptureService remoteService)
- throws RemoteException {
- remoteService.mService.onContentCaptureEventsRequest(mSessionId,
- new ContentCaptureEventsRequest(mEvents));
- }
- }
-
- private static final class PendingOnActivitySnapshotRequest extends MyPendingRequest {
-
- private final SnapshotData mSnapshotData;
-
- protected PendingOnActivitySnapshotRequest(@NonNull RemoteContentCaptureService service,
- @NonNull String sessionId, @NonNull SnapshotData snapshotData) {
- super(service, sessionId);
- mSnapshotData = snapshotData;
- }
-
- @Override // from MyPendingRequest
- protected void myRun(@NonNull RemoteContentCaptureService remoteService)
- throws RemoteException {
- remoteService.mService.onActivitySnapshot(mSessionId, mSnapshotData);
- }
- }
-
- public interface ContentCaptureServiceCallbacks extends VultureCallback {
- // To keep it simple, we use the same callback for all failures / timeouts.
- void onFailureOrTimeout(boolean timedOut);
+ public interface ContentCaptureServiceCallbacks
+ extends VultureCallback<RemoteContentCaptureService> {
+ // NOTE: so far we don't need to notify the callback implementation (an inner class on
+ // AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
+ // callback interface is empty.
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index cccacf4..4408db8 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -7,7 +7,6 @@
"frameworks/native/aidl/binder",
"frameworks/native/cmds/dumpstate/binder",
"system/core/storaged/binder",
- "system/netd/server/binder",
"system/vold/binder",
],
},
@@ -21,7 +20,6 @@
":mediaupdateservice_aidl",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
- ":netd_metrics_aidl",
],
libs: [
@@ -52,6 +50,7 @@
"android.hardware.contexthub-V1.0-java",
"android.hidl.manager-V1.0-java",
"netd_aidl_interface-java",
+ "netd_event_listener_interface-java",
],
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 854c03f..08034f7 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -48,6 +48,7 @@
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -1306,8 +1307,12 @@
// because kernel doesn't keep this after reboot
setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
- // Also sure that we're booting with a halfway sensible current time
- final long systemBuildTime = Environment.getRootDirectory().lastModified();
+ // Ensure that we're booting with a halfway sensible current time. Use the
+ // most recent of Build.TIME, the root file system's timestamp, and the
+ // value of the ro.build.date.utc system property (which is in seconds).
+ final long systemBuildTime = Long.max(
+ 1000L * SystemProperties.getLong("ro.build.date.utc", -1L),
+ Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
if (mInjector.getCurrentTimeMillis() < systemBuildTime) {
Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis()
+ ", advancing to build time " + systemBuildTime);
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
index d564925..8c5ee7f 100644
--- a/services/core/java/com/android/server/AnyMotionDetector.java
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -26,8 +26,6 @@
import android.os.SystemClock;
import android.util.Slog;
-import java.lang.Float;
-
/**
* Determines if the device has been set upon a stationary object.
*/
@@ -140,6 +138,13 @@
}
}
+ /**
+ * If we do not have an accelerometer, we are not going to collect much data.
+ */
+ public boolean hasSensor() {
+ return mAccelSensor != null;
+ }
+
/*
* Acquire accel data until we determine AnyMotion status.
*/
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index af9d4c8..08cb7a2 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -43,6 +43,7 @@
import android.net.INetworkPolicyManager;
import android.net.NetworkInfo;
import android.net.Uri;
+import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
@@ -272,6 +273,7 @@
private PowerManager mPowerManager;
private INetworkPolicyManager mNetworkPolicyManager;
private SensorManager mSensorManager;
+ private final boolean mUseMotionSensor;
private Sensor mMotionSensor;
private LocationRequest mLocationRequest;
private Intent mIdleIntent;
@@ -520,9 +522,10 @@
updateConnectivityState(intent);
} break;
case Intent.ACTION_BATTERY_CHANGED: {
+ boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
+ boolean plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
synchronized (DeviceIdleController.this) {
- int plugged = intent.getIntExtra("plugged", 0);
- updateChargingLocked(plugged != 0);
+ updateChargingLocked(present && plugged);
}
} break;
case Intent.ACTION_PACKAGE_REMOVED: {
@@ -1629,6 +1632,9 @@
mHandler = mInjector.getHandler(this);
mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper());
LocalServices.addService(AppStateTracker.class, mAppStateTracker);
+
+ mUseMotionSensor = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModeUseMotionSensor);
}
public DeviceIdleController(Context context) {
@@ -1729,20 +1735,23 @@
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
- int sigMotionSensorId = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
- if (sigMotionSensorId > 0) {
- mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
- }
- if (mMotionSensor == null && getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
- mMotionSensor = mSensorManager.getDefaultSensor(
- Sensor.TYPE_WRIST_TILT_GESTURE, true);
- }
- if (mMotionSensor == null) {
- // As a last ditch, fall back to SMD.
- mMotionSensor = mSensorManager.getDefaultSensor(
- Sensor.TYPE_SIGNIFICANT_MOTION, true);
+
+ if (mUseMotionSensor) {
+ int sigMotionSensorId = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
+ if (sigMotionSensorId > 0) {
+ mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
+ }
+ if (mMotionSensor == null && getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_WRIST_TILT_GESTURE, true);
+ }
+ if (mMotionSensor == null) {
+ // As a last ditch, fall back to SMD.
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_SIGNIFICANT_MOTION, true);
+ }
}
if (getContext().getResources().getBoolean(
@@ -2588,14 +2597,21 @@
mState = STATE_SENSING;
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
EventLogTags.writeDeviceIdle(mState, reason);
- scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
cancelLocatingLocked();
- mNotMoving = false;
mLocated = false;
mLastGenericLocation = null;
mLastGpsLocation = null;
- mAnyMotionDetector.checkForAnyMotion();
- break;
+
+ // If we have an accelerometer, wait to find out whether we are moving.
+ if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) {
+ scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
+ mNotMoving = false;
+ mAnyMotionDetector.checkForAnyMotion();
+ break;
+ }
+
+ mNotMoving = true;
+ // Otherwise, fall through and check this off the list of requirements.
case STATE_SENSING:
cancelSensingTimeoutAlarmLocked();
mState = STATE_LOCATING;
@@ -2893,9 +2909,12 @@
void scheduleAlarmLocked(long delay, boolean idleUntil) {
if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
- if (mMotionSensor == null && !(mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE
- || mState == STATE_IDLE_MAINTENANCE)) {
- // If there is no motion sensor on this device, then we won't schedule
+
+ if (mUseMotionSensor && mMotionSensor == null
+ && mState != STATE_QUICK_DOZE_DELAY
+ && mState != STATE_IDLE
+ && mState != STATE_IDLE_MAINTENANCE) {
+ // If there is no motion sensor on this device, but we need one, then we won't schedule
// alarms, because we can't determine if the device is not moving. This effectively
// turns off normal execution of device idling, although it is still possible to
// manually poke it by pretending like the alarm is going off.
@@ -3765,13 +3784,20 @@
pw.print(" mLightEnabled="); pw.print(mLightEnabled);
pw.print(" mDeepEnabled="); pw.println(mDeepEnabled);
pw.print(" mForceIdle="); pw.println(mForceIdle);
- pw.print(" mMotionSensor="); pw.println(mMotionSensor);
+ pw.print(" mUseMotionSensor="); pw.print(mUseMotionSensor);
+ if (mUseMotionSensor) {
+ pw.print(" mMotionSensor="); pw.println(mMotionSensor);
+ } else {
+ pw.println();
+ }
pw.print(" mScreenOn="); pw.println(mScreenOn);
pw.print(" mScreenLocked="); pw.println(mScreenLocked);
pw.print(" mNetworkConnected="); pw.println(mNetworkConnected);
pw.print(" mCharging="); pw.println(mCharging);
pw.print(" mMotionActive="); pw.println(mMotionListener.active);
- pw.print(" mNotMoving="); pw.println(mNotMoving);
+ if (mUseMotionSensor) {
+ pw.print(" mNotMoving="); pw.println(mNotMoving);
+ }
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
pw.print(mHasGps); pw.print(" mHasNetwork=");
pw.print(mHasNetworkLocation); pw.print(" mLocated="); pw.println(mLocated);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 87a42fa..4678fec 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -956,18 +956,6 @@
// INetworkManagementService members
//
@Override
- public INetd getNetdService() throws RemoteException {
- final CountDownLatch connectedSignal = mConnectedSignal;
- if (connectedSignal != null) {
- try {
- connectedSignal.await();
- } catch (InterruptedException ignored) {}
- }
-
- return mNetdService;
- }
-
- @Override
public String[] listInterfaces() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index bbb1d13..9f353a8 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -16,7 +16,9 @@
package com.android.server;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -111,6 +113,7 @@
private final boolean mSupportsAmplitudeControl;
private final int mDefaultVibrationAmplitude;
private final SparseArray<VibrationEffect> mFallbackEffects;
+ private final SparseArray<Integer> mProcStatesCache = new SparseArray();
private final WorkSource mTmpWorkSource = new WorkSource();
private final Handler mH = new Handler();
private final Object mLock = new Object();
@@ -147,6 +150,25 @@
native static void vibratorSetAmplitude(int amplitude);
native static long vibratorPerformEffect(long effect, long strength);
+ private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+ mProcStatesCache.put(uid, procState);
+ }
+
+ @Override public void onUidGone(int uid, boolean disabled) {
+ mProcStatesCache.delete(uid);
+ }
+
+ @Override public void onUidActive(int uid) {
+ }
+
+ @Override public void onUidIdle(int uid, boolean disabled) {
+ }
+
+ @Override public void onUidCachedChanged(int uid, boolean cached) {
+ }
+ };
+
private class Vibration implements IBinder.DeathRecipient {
public final IBinder token;
// Start time in CLOCK_BOOTTIME base.
@@ -411,6 +433,14 @@
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
+ try {
+ ActivityManager.getService().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+
updateVibrators();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -502,6 +532,12 @@
return;
}
verifyIncomingUid(uid);
+ if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
+ > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ Slog.e(TAG, "Ignoring incoming vibration as process with uid = "
+ + uid + " is background");
+ return;
+ }
if (!verifyVibrationEffect(effect)) {
return;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 754e72e..69cfcc2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2724,47 +2724,86 @@
return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
- void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
+ /**
+ * Update battery stats on the activity' usage.
+ * @param activity
+ * @param uid
+ * @param userId
+ * @param resumed
+ */
+ void updateBatteryStats(ComponentName activity, int uid, int userId, boolean resumed) {
if (DEBUG_SWITCH) {
Slog.d(TAG_SWITCH,
- "updateUsageStats: comp=" + activity + "res=" + resumed);
+ "updateBatteryStats: comp=" + activity + "res=" + resumed);
}
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
- uid, activity.getPackageName(),
- activity.getShortClassName(), resumed ?
- StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
+ uid, activity.getPackageName(), activity.getShortClassName(),
+ resumed ? StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
- if (resumed) {
- if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(activity, userId,
- UsageEvents.Event.MOVE_TO_FOREGROUND);
-
- }
- synchronized (stats) {
+ synchronized (stats) {
+ if (resumed) {
stats.noteActivityResumedLocked(uid);
- }
- } else {
- if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(activity, userId,
- UsageEvents.Event.MOVE_TO_BACKGROUND);
- }
- synchronized (stats) {
+ } else {
stats.noteActivityPausedLocked(uid);
}
}
}
+ /**
+ * Update UsageStas on the activity's usage.
+ * @param activity
+ * @param userId
+ * @param event
+ * @param appToken ActivityRecord's appToken.
+ */
+ public void updateActivityUsageStats(ComponentName activity, int userId, int event,
+ IBinder appToken) {
+ if (DEBUG_SWITCH) {
+ Slog.d(TAG_SWITCH, "updateActivityUsageStats: comp="
+ + activity + " hash=" + appToken.hashCode() + " event=" + event);
+ }
+ synchronized (this) {
+ if (mUsageStatsService != null) {
+ mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode());
+ }
+ }
+ }
+
+ /**
+ * Update UsageStats on this package's usage.
+ * @param packageName
+ * @param userId
+ * @param event
+ */
+ public void updateActivityUsageStats(String packageName, int userId, int event) {
+ if (DEBUG_SWITCH) {
+ Slog.d(TAG_SWITCH, "updateActivityUsageStats: package="
+ + packageName + " event=" + event);
+ }
+ synchronized (this) {
+ if (mUsageStatsService != null) {
+ mUsageStatsService.reportEvent(packageName, userId, event);
+ }
+ }
+ }
+
+ /**
+ * Update Usages on this foreground service's usage.
+ * @param service
+ * @param userId
+ * @param started
+ */
void updateForegroundServiceUsageStats(ComponentName service, int userId, boolean started) {
if (DEBUG_SWITCH) {
Slog.d(TAG_SWITCH, "updateForegroundServiceUsageStats: comp="
- + service + "started=" + started);
+ + service + " started=" + started);
}
synchronized (this) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(service, userId,
started ? UsageEvents.Event.FOREGROUND_SERVICE_START
- : UsageEvents.Event.FOREGROUND_SERVICE_STOP);
+ : UsageEvents.Event.FOREGROUND_SERVICE_STOP, 0);
}
}
}
@@ -7608,7 +7647,7 @@
String extraOptions = null;
switch (bugreportType) {
case ActivityManager.BUGREPORT_OPTION_FULL:
- // Default options.
+ extraOptions = "bugreportfull";
break;
case ActivityManager.BUGREPORT_OPTION_INTERACTIVE:
extraOptions = "bugreportplus";
@@ -18962,7 +19001,7 @@
ProcessMemoryState processMemoryState =
new ProcessMemoryState(uid,
r.processName,
- r.maxAdj,
+ r.curAdj,
memoryStat.pgfault,
memoryStat.pgmajfault,
memoryStat.rssInBytes,
@@ -19068,9 +19107,19 @@
}
@Override
- public void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
+ public void updateBatteryStats(ComponentName activity, int uid, int userId,
+ boolean resumed) {
synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.updateUsageStats(activity, uid, userId, resumed);
+ ActivityManagerService.this.updateBatteryStats(activity, uid, userId, resumed);
+ }
+ }
+
+ @Override
+ public void updateActivityUsageStats(ComponentName activity, int userId, int event,
+ IBinder appToken) {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.updateActivityUsageStats(activity, userId, event,
+ appToken);
}
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 9649ccd..32219aa 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -282,9 +282,11 @@
public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
IBinder token, ServiceListener listener, int userId, int groupId,
- byte[] cryptoToken, boolean restricted, String owner) {
+ byte[] cryptoToken, boolean restricted, String owner,
+ final int[] disabledFeatures) {
super(context, getMetrics(), daemon, halDeviceId, token, listener,
- userId, groupId, cryptoToken, restricted, owner, getBiometricUtils());
+ userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(),
+ disabledFeatures);
}
@Override
@@ -408,7 +410,8 @@
int cancel() throws RemoteException;
int remove(int groupId, int biometricId) throws RemoteException;
int enumerate() throws RemoteException;
- int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException;
+ int enroll(byte[] cryptoToken, int groupId, int timeout,
+ ArrayList<Integer> disabledFeatures) throws RemoteException;
}
/**
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index f858ef5..8a0f085 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -34,15 +34,18 @@
private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
private final byte[] mCryptoToken;
private final BiometricUtils mBiometricUtils;
+ private final int[] mDisabledFeatures;
public EnrollClient(Context context, Metrics metrics,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int userId, int groupId,
- byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils) {
+ byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils,
+ final int[] disabledFeatures) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
owner, 0 /* cookie */);
mBiometricUtils = utils;
mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
+ mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
}
@Override
@@ -74,7 +77,13 @@
public int start() {
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
- final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout);
+ final ArrayList<Integer> disabledFeatures = new ArrayList<>();
+ for (int i = 0; i < mDisabledFeatures.length; i++) {
+ disabledFeatures.add(mDisabledFeatures[i]);
+ }
+
+ final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout,
+ disabledFeatures);
if (result != 0) {
Slog.w(getLogTag(), "startEnroll failed, result=" + result);
mMetricsLogger.histogram(mMetrics.tagEnrollStartError(), result);
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 557af04..72f73f6 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -113,17 +113,17 @@
}
@Override // Binder call
- public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
- final IFaceServiceReceiver receiver, final int flags,
- final String opPackageName) {
+ public void enroll(final IBinder token, final byte[] cryptoToken,
+ final IFaceServiceReceiver receiver, final String opPackageName,
+ final int[] disabledFeatures) {
checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = isRestricted();
final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
- 0 /* groupId */, cryptoToken, restricted, opPackageName);
+ 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures);
- enrollInternal(client, userId);
+ enrollInternal(client, UserHandle.getCallingUserId());
}
@Override // Binder call
@@ -333,7 +333,7 @@
}
@Override
- public int setRequireAttention(boolean requireAttention, final byte[] token) {
+ public int setFeature(int feature, boolean enabled, final byte[] token) {
checkPermission(MANAGE_BIOMETRIC);
final ArrayList<Byte> byteToken = new ArrayList<>();
@@ -343,10 +343,11 @@
int result;
try {
- result = mDaemon != null ? mDaemon.setRequireAttention(requireAttention, byteToken)
+ result = mDaemon != null ? mDaemon.setFeature(feature, enabled, byteToken)
: Status.INTERNAL_ERROR;
} catch (RemoteException e) {
- Slog.e(getTag(), "Unable to setRequireAttention to " + requireAttention, e);
+ Slog.e(getTag(), "Unable to set feature: " + feature + " to enabled:" + enabled,
+ e);
result = Status.INTERNAL_ERROR;
}
@@ -354,17 +355,12 @@
}
@Override
- public boolean getRequireAttention(final byte[] token) {
+ public boolean getFeature(int feature) {
checkPermission(MANAGE_BIOMETRIC);
- final ArrayList<Byte> byteToken = new ArrayList<>();
- for (int i = 0; i < token.length; i++) {
- byteToken.add(token[i]);
- }
-
boolean result = true;
try {
- result = mDaemon != null ? mDaemon.getRequireAttention(byteToken).value : true;
+ result = mDaemon != null ? mDaemon.getFeature(feature) : true;
} catch (RemoteException e) {
Slog.e(getTag(), "Unable to getRequireAttention", e);
}
@@ -612,7 +608,8 @@
}
@Override
- public int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException {
+ public int enroll(byte[] cryptoToken, int groupId, int timeout,
+ ArrayList<Integer> disabledFeatures) throws RemoteException {
IBiometricsFace daemon = getFaceDaemon();
if (daemon == null) {
Slog.w(TAG, "enroll(): no face HAL!");
@@ -623,7 +620,7 @@
token.add(cryptoToken[i]);
}
// TODO: plumb requireAttention down from framework
- return daemon.enroll(token, timeout, true /* requireAttention */);
+ return daemon.enroll(token, timeout, disabledFeatures);
}
};
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 6a5bc61..3895ef7 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -144,7 +144,7 @@
final int groupId = userId; // default group for fingerprint enrollment
final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId,
- cryptoToken, restricted, opPackageName);
+ cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */);
enrollInternal(client, userId);
}
@@ -716,7 +716,8 @@
}
@Override
- public int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException {
+ public int enroll(byte[] cryptoToken, int groupId, int timeout,
+ ArrayList<Integer> disabledFeatures) throws RemoteException {
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "enroll(): no fingerprint HAL!");
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 360a7d1..cf8d21b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -46,6 +46,8 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
import android.hardware.display.DisplayViewport;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
import android.hardware.display.IDisplayManager;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
@@ -1241,6 +1243,29 @@
}
}
+ @VisibleForTesting
+ DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributesInternal(
+ int displayId) {
+ IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
+ return SurfaceControl.getDisplayedContentSamplingAttributes(displayToken);
+ }
+
+ @VisibleForTesting
+ boolean setDisplayedContentSamplingEnabledInternal(
+ int displayId, boolean enable, int componentMask, int maxFrames) {
+ IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
+ return SurfaceControl.setDisplayedContentSamplingEnabled(
+ displayToken, enable, componentMask, maxFrames);
+ }
+
+ @VisibleForTesting
+ DisplayedContentSample getDisplayedContentSampleInternal(int displayId,
+ long maxFrames, long timestamp) {
+ IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
+ return SurfaceControl.getDisplayedContentSample(
+ displayToken, maxFrames, timestamp);
+ }
+
private void clearViewportsLocked() {
mViewports.clear();
}
@@ -2331,5 +2356,25 @@
}
}
}
+
+ @Override
+ public DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributes(
+ int displayId) {
+ return getDisplayedContentSamplingAttributesInternal(displayId);
+ }
+
+ @Override
+ public boolean setDisplayedContentSamplingEnabled(
+ int displayId, boolean enable, int componentMask, int maxFrames) {
+ return setDisplayedContentSamplingEnabledInternal(
+ displayId, enable, componentMask, maxFrames);
+ }
+
+ @Override
+ public DisplayedContentSample getDisplayedContentSample(int displayId,
+ long maxFrames, long timestamp) {
+ return getDisplayedContentSampleInternal(displayId, maxFrames, timestamp);
+ }
+
}
}
diff --git a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
index 513a6a3..aaea45e 100644
--- a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
+import android.os.IInterface;
import android.util.Slog;
import java.io.PrintWriter;
@@ -29,21 +30,21 @@
* bound.
*
* @param <S> the concrete remote service class
- *
+ * @param <I> the interface of the binder service
* @hide
*/
-public abstract class AbstractMultiplePendingRequestsRemoteService<
- S extends AbstractMultiplePendingRequestsRemoteService<S>>
- extends AbstractRemoteService<S> {
+public abstract class AbstractMultiplePendingRequestsRemoteService<S
+ extends AbstractMultiplePendingRequestsRemoteService<S, I>, I extends IInterface>
+ extends AbstractRemoteService<S, I> {
private final int mInitialCapacity;
- protected ArrayList<PendingRequest<S>> mPendingRequests;
+ protected ArrayList<PendingRequest<S, I>> mPendingRequests;
public AbstractMultiplePendingRequestsRemoteService(@NonNull Context context,
@NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
- @NonNull VultureCallback callback, boolean bindInstantServiceAllowed, boolean verbose,
- int initialCapacity) {
+ @NonNull VultureCallback<S> callback, boolean bindInstantServiceAllowed,
+ boolean verbose, int initialCapacity) {
super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
verbose);
mInitialCapacity = initialCapacity;
@@ -84,7 +85,7 @@
}
@Override // from AbstractRemoteService
- void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+ void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) {
if (mPendingRequests == null) {
mPendingRequests = new ArrayList<>(mInitialCapacity);
}
diff --git a/services/core/java/com/android/server/infra/AbstractRemoteService.java b/services/core/java/com/android/server/infra/AbstractRemoteService.java
index 67b3ecf..41dcf89 100644
--- a/services/core/java/com/android/server/infra/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractRemoteService.java
@@ -54,13 +54,13 @@
* (no pun intended) example of how to use it.
*
* @param <S> the concrete remote service class
+ * @param <I> the interface of the binder service
*
* @hide
*/
//TODO(b/117779333): improve javadoc above instead of using Autofill as an example
-public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
- implements DeathRecipient {
-
+public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I>,
+ I extends IInterface> implements DeathRecipient {
private static final int MSG_UNBIND = 1;
protected static final int LAST_PRIVATE_MSG = MSG_UNBIND;
@@ -74,11 +74,11 @@
private final Context mContext;
private final Intent mIntent;
- private final VultureCallback mVultureCallback;
+ private final VultureCallback<S> mVultureCallback;
private final int mUserId;
private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
private final boolean mBindInstantServiceAllowed;
- private IInterface mServiceInterface;
+ protected I mService;
private boolean mBinding;
private boolean mDestroyed;
@@ -87,19 +87,21 @@
/**
* Callback called when the service dies.
+ *
+ * @param <T> service class
*/
- public interface VultureCallback {
+ public interface VultureCallback<T> {
/**
* Called when the service dies.
*
* @param service service that died!
*/
- void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service);
+ void onServiceDied(T service);
}
// NOTE: must be package-protected so this class is not extend outside
AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
- @NonNull ComponentName componentName, int userId, @NonNull VultureCallback callback,
+ @NonNull ComponentName componentName, int userId, @NonNull VultureCallback<S> callback,
boolean bindInstantServiceAllowed, boolean verbose) {
mContext = context;
mVultureCallback = callback;
@@ -150,7 +152,7 @@
* Gets the base Binder interface from the service.
*/
@NonNull
- protected abstract IInterface getServiceInterface(@NonNull IBinder service);
+ protected abstract I getServiceInterface(@NonNull IBinder service);
/**
* Defines How long after the last interaction with the service we would unbind.
@@ -183,12 +185,14 @@
private void handleBinderDied() {
if (checkIfDestroyed()) return;
- if (mServiceInterface != null) {
- mServiceInterface.asBinder().unlinkToDeath(this, 0);
+ if (mService != null) {
+ mService.asBinder().unlinkToDeath(this, 0);
}
- mServiceInterface = null;
+ mService = null;
mServiceDied = true;
- mVultureCallback.onServiceDied(this);
+ @SuppressWarnings("unchecked") // TODO(b/117779333): fix this warning
+ final S castService = (S) this;
+ mVultureCallback.onServiceDied(castService);
}
// Note: we are dumping without a lock held so this is a bit racy but
@@ -216,12 +220,35 @@
pw.println();
}
- protected void scheduleRequest(@NonNull PendingRequest<S> pendingRequest) {
+ /**
+ * Schedules a "sync" request.
+ *
+ * <p>This request must be responded by the service somehow (typically using a callback),
+ * othewise it will trigger a {@link PendingRequest#onTimeout(AbstractRemoteService)} if the
+ * service doesn't respond.
+ */
+ protected void scheduleRequest(@NonNull PendingRequest<S, I> pendingRequest) {
+ cancelScheduledUnbind();
mHandler.sendMessage(obtainMessage(
AbstractRemoteService::handlePendingRequest, this, pendingRequest));
}
- protected void cancelScheduledUnbind() {
+ /**
+ * Schedules an async request.
+ *
+ * <p>This request is not expecting a callback from the service, hence it's represented by
+ * a simple {@link Runnable}.
+ */
+ protected void scheduleAsyncRequest(@NonNull AsyncRequest<I> request) {
+ cancelScheduledUnbind();
+ // TODO(b/117779333): fix generics below
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request);
+ mHandler.sendMessage(
+ obtainMessage(AbstractRemoteService::handlePendingRequest, this, asyncRequest));
+ }
+
+ private void cancelScheduledUnbind() {
mHandler.removeMessages(MSG_UNBIND);
}
@@ -244,7 +271,7 @@
* Handles a request, either processing it right now when bound, or saving it to be handled when
* bound.
*/
- protected final void handlePendingRequest(@NonNull PendingRequest<S> pendingRequest) {
+ protected final void handlePendingRequest(@NonNull PendingRequest<S, I> pendingRequest) {
if (checkIfDestroyed() || mCompleted) return;
if (!handleIsBound()) {
@@ -263,10 +290,10 @@
/**
* Defines what to do with a request that arrives while not bound to the service.
*/
- abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest);
+ abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest);
private boolean handleIsBound() {
- return mServiceInterface != null;
+ return mService != null;
}
private void handleEnsureBound() {
@@ -300,9 +327,9 @@
mBinding = false;
if (handleIsBound()) {
handleOnConnectedStateChangedInternal(false);
- if (mServiceInterface != null) {
- mServiceInterface.asBinder().unlinkToDeath(this, 0);
- mServiceInterface = null;
+ if (mService != null) {
+ mService.asBinder().unlinkToDeath(this, 0);
+ mService = null;
}
}
mContext.unbindService(mServiceConnection);
@@ -318,7 +345,7 @@
return;
}
mBinding = false;
- mServiceInterface = getServiceInterface(service);
+ mService = getServiceInterface(service);
try {
service.linkToDeath(AbstractRemoteService.this, 0);
} catch (RemoteException re) {
@@ -332,7 +359,7 @@
@Override
public void onServiceDisconnected(ComponentName name) {
mBinding = true;
- mServiceInterface = null;
+ mService = null;
}
}
@@ -349,10 +376,15 @@
/**
* Base class for the requests serviced by the remote service.
*
+ * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to
+ * communicate back with the system server. For cases where that's not needed, you should use
+ * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead.
+ *
* @param <S> the remote service class
+ * @param <I> the interface of the binder service
*/
- public abstract static class PendingRequest<S extends AbstractRemoteService<S>>
- implements Runnable {
+ public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>,
+ I extends IInterface> implements Runnable {
protected final String mTag = getClass().getSimpleName();
protected final Object mLock = new Object();
@@ -366,7 +398,7 @@
@GuardedBy("mLock")
private boolean mCompleted;
- protected PendingRequest(S service) {
+ protected PendingRequest(@NonNull S service) {
mWeakService = new WeakReference<>(service);
mServiceHandler = service.mHandler;
mTimeoutTrigger = () -> {
@@ -452,4 +484,50 @@
return false;
}
}
+
+ /**
+ * Represents a request that does not expect a callback from the remote service.
+ *
+ * @param <I> the interface of the binder service
+ */
+ public interface AsyncRequest<I extends IInterface> {
+
+ /**
+ * Run Forrest, run!
+ */
+ void run(@NonNull I binder) throws RemoteException;
+ }
+
+ private static final class MyAsyncPendingRequest<S extends AbstractRemoteService<S, I>,
+ I extends IInterface> extends PendingRequest<S, I> {
+ private static final String TAG = MyAsyncPendingRequest.class.getSimpleName();
+
+ private final AsyncRequest<I> mRequest;
+
+ protected MyAsyncPendingRequest(@NonNull S service, @NonNull AsyncRequest<I> request) {
+ super(service);
+
+ mRequest = request;
+ }
+
+ @Override
+ public void run() {
+ final S remoteService = getService();
+ if (remoteService == null) return;
+ try {
+ mRequest.run(remoteService.mService);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "exception handling async request (" + this + "): " + e);
+ } finally {
+ finish();
+ }
+ }
+
+ @Override
+ protected void onTimeout(S remoteService) {
+ // TODO(b/117779333): should not happen because we called finish() on run(), although
+ // currently it might be called if the service is destroyed while showing it.
+ Slog.w(TAG, "AsyncPending requested timed out");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
index 37a1f54..d32f13b 100644
--- a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
+import android.os.IInterface;
import android.util.Slog;
import java.io.PrintWriter;
@@ -29,17 +30,19 @@
* <p>If another request is received while not bound, the previous one will be canceled.
*
* @param <S> the concrete remote service class
+ * @param <I> the interface of the binder service
*
* @hide
*/
-public abstract class AbstractSinglePendingRequestRemoteService<
- S extends AbstractSinglePendingRequestRemoteService<S>> extends AbstractRemoteService<S> {
+public abstract class AbstractSinglePendingRequestRemoteService<S
+ extends AbstractSinglePendingRequestRemoteService<S, I>, I extends IInterface>
+ extends AbstractRemoteService<S, I> {
- protected PendingRequest<S> mPendingRequest;
+ protected PendingRequest<S, I> mPendingRequest;
public AbstractSinglePendingRequestRemoteService(@NonNull Context context,
@NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
- @NonNull VultureCallback callback, boolean bindInstantServiceAllowed,
+ @NonNull VultureCallback<S> callback, boolean bindInstantServiceAllowed,
boolean verbose) {
super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
verbose);
@@ -48,7 +51,7 @@
@Override // from AbstractRemoteService
void handlePendingRequests() {
if (mPendingRequest != null) {
- final PendingRequest<S> pendingRequest = mPendingRequest;
+ final PendingRequest<S, I> pendingRequest = mPendingRequest;
mPendingRequest = null;
handlePendingRequest(pendingRequest);
}
@@ -70,7 +73,7 @@
}
@Override // from AbstractRemoteService
- void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+ void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) {
if (mPendingRequest != null) {
if (mVerbose) {
Slog.v(mTag, "handlePendingRequestWhileUnBound(): cancelling " + mPendingRequest
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 611c8b7..78e18e9 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -839,6 +839,15 @@
break;
}
}
+ if (DEBUG) {
+ Slog.d(TAG, "Something in " + pkgName
+ + " changed. Reevaluating controller states.");
+ }
+ synchronized (mLock) {
+ for (int c = mControllers.size() - 1; c >= 0; --c) {
+ mControllers.get(c).reevaluateStateLocked(pkgUid);
+ }
+ }
}
} else {
Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
@@ -1042,6 +1051,8 @@
mJobPackageTracker.notePending(jobStatus);
addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
maybeRunPendingJobsLocked();
+ } else {
+ evaluateControllerStatesLocked(jobStatus);
}
}
return JobScheduler.RESULT_SUCCESS;
@@ -1884,6 +1895,8 @@
newReadyJobs = new ArrayList<JobStatus>();
}
newReadyJobs.add(job);
+ } else {
+ evaluateControllerStatesLocked(job);
}
}
@@ -1957,6 +1970,8 @@
runnableJobs = new ArrayList<>();
}
runnableJobs.add(job);
+ } else {
+ evaluateControllerStatesLocked(job);
}
}
@@ -2087,6 +2102,15 @@
HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
}
+ /** Returns true if both the calling and source users for the job are started. */
+ private boolean areUsersStartedLocked(final JobStatus job) {
+ boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
+ if (job.getUserId() == job.getSourceUserId()) {
+ return sourceStarted;
+ }
+ return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId());
+ }
+
/**
* Criteria for moving a job into the pending queue:
* - It's ready.
@@ -2219,6 +2243,61 @@
return componentPresent;
}
+ private void evaluateControllerStatesLocked(final JobStatus job) {
+ for (int c = mControllers.size() - 1; c >= 0; --c) {
+ final StateController sc = mControllers.get(c);
+ sc.evaluateStateLocked(job);
+ }
+ }
+
+ /**
+ * Returns true if non-job constraint components are in place -- if job.isReady() returns true
+ * and this method returns true, then the job is ready to be executed.
+ */
+ public boolean areComponentsInPlaceLocked(JobStatus job) {
+ // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same
+ // conditions.
+
+ final boolean jobExists = mJobs.containsJob(job);
+ final boolean userStarted = areUsersStartedLocked(job);
+
+ if (DEBUG) {
+ Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
+ + " exists=" + jobExists + " userStarted=" + userStarted);
+ }
+
+ // These are also fairly cheap to check, though they typically will not
+ // be conditions we fail.
+ if (!jobExists || !userStarted) {
+ return false;
+ }
+
+ // Job pending/active doesn't affect the readiness of a job.
+
+ // Skipping the hearbeat check as this will only come into play when using the rolling
+ // window quota management system.
+
+ // The expensive check last: validate that the defined package+service is
+ // still present & viable.
+ final boolean componentPresent;
+ try {
+ // TODO: cache result until we're notified that something in the package changed.
+ componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
+ job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ job.getUserId()) != null);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
+ + " componentPresent=" + componentPresent);
+ }
+
+ // Everything else checked out so far, so this is the final yes/no check
+ return componentPresent;
+ }
+
/**
* Reconcile jobs in the pending queue against available execution contexts.
* A controller can force a job into the pending queue even if it's already running, but
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 6989c33..8f104e4 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -41,10 +41,12 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobSchedulerService.Constants;
import com.android.server.job.JobServiceContext;
import com.android.server.job.StateControllerProto;
+import com.android.server.net.NetworkPolicyManagerInternal;
import java.util.Objects;
import java.util.function.Predicate;
@@ -66,16 +68,29 @@
private final ConnectivityManager mConnManager;
private final NetworkPolicyManager mNetPolicyManager;
+ private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;
/** List of tracked jobs keyed by source UID. */
@GuardedBy("mLock")
private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>();
+ /**
+ * Keep track of all the UID's jobs that the controller has requested that NetworkPolicyManager
+ * grant an exception to in the app standby chain.
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<ArraySet<JobStatus>> mRequestedWhitelistJobs = new SparseArray<>();
+
+ /** List of currently available networks. */
+ @GuardedBy("mLock")
+ private final ArraySet<Network> mAvailableNetworks = new ArraySet<>();
+
public ConnectivityController(JobSchedulerService service) {
super(service);
mConnManager = mContext.getSystemService(ConnectivityManager.class);
mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
+ mNetPolicyManagerInternal = LocalServices.getService(NetworkPolicyManagerInternal.class);
// We're interested in all network changes; internally we match these
// network changes against the active network for each UID with jobs.
@@ -109,9 +124,178 @@
if (jobs != null) {
jobs.remove(jobStatus);
}
+ maybeRevokeStandbyExceptionLocked(jobStatus);
}
}
+ @GuardedBy("mLock")
+ @Override
+ public void onConstantsUpdatedLocked() {
+ if (mConstants.USE_HEARTBEATS) {
+ // App idle exceptions are only requested for the rolling quota system.
+ if (DEBUG) Slog.i(TAG, "Revoking all standby exceptions");
+ for (int i = 0; i < mRequestedWhitelistJobs.size(); ++i) {
+ int uid = mRequestedWhitelistJobs.keyAt(i);
+ mNetPolicyManagerInternal.setAppIdleWhitelist(uid, false);
+ }
+ mRequestedWhitelistJobs.clear();
+ }
+ }
+
+ /**
+ * Returns true if the job's requested network is available. This DOES NOT necesarilly mean
+ * that the UID has been granted access to the network.
+ */
+ public boolean isNetworkAvailable(JobStatus job) {
+ synchronized (mLock) {
+ for (int i = 0; i < mAvailableNetworks.size(); ++i) {
+ final Network network = mAvailableNetworks.valueAt(i);
+ final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(
+ network);
+ final boolean satisfied = isSatisfied(job, network, capabilities, mConstants);
+ if (DEBUG) {
+ Slog.v(TAG, "isNetworkAvailable(" + job + ") with network " + network
+ + " and capabilities " + capabilities + ". Satisfied=" + satisfied);
+ }
+ if (satisfied) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Request that NetworkPolicyManager grant an exception to the uid from its standby policy
+ * chain.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ void requestStandbyExceptionLocked(JobStatus job) {
+ final int uid = job.getSourceUid();
+ // Need to call this before adding the job.
+ final boolean isExceptionRequested = isStandbyExceptionRequestedLocked(uid);
+ ArraySet<JobStatus> jobs = mRequestedWhitelistJobs.get(uid);
+ if (jobs == null) {
+ jobs = new ArraySet<JobStatus>();
+ mRequestedWhitelistJobs.put(uid, jobs);
+ }
+ if (!jobs.add(job) || isExceptionRequested) {
+ if (DEBUG) {
+ Slog.i(TAG, "requestStandbyExceptionLocked found exception already requested.");
+ }
+ return;
+ }
+ if (DEBUG) Slog.i(TAG, "Requesting standby exception for UID: " + uid);
+ mNetPolicyManagerInternal.setAppIdleWhitelist(uid, true);
+ }
+
+ /** Returns whether a standby exception has been requested for the UID. */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ boolean isStandbyExceptionRequestedLocked(final int uid) {
+ ArraySet jobs = mRequestedWhitelistJobs.get(uid);
+ return jobs != null && jobs.size() > 0;
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ boolean wouldBeReadyWithConnectivityLocked(JobStatus jobStatus) {
+ final boolean networkAvailable = isNetworkAvailable(jobStatus);
+ if (DEBUG) {
+ Slog.v(TAG, "wouldBeReadyWithConnectivityLocked: " + jobStatus.toShortString()
+ + " networkAvailable=" + networkAvailable);
+ }
+ // If the network isn't available, then requesting an exception won't help.
+
+ return networkAvailable && wouldBeReadyWithConstraintLocked(jobStatus,
+ JobStatus.CONSTRAINT_CONNECTIVITY);
+ }
+
+ /**
+ * Tell NetworkPolicyManager not to block a UID's network connection if that's the only
+ * thing stopping a job from running.
+ */
+ @GuardedBy("mLock")
+ @Override
+ public void evaluateStateLocked(JobStatus jobStatus) {
+ if (mConstants.USE_HEARTBEATS) {
+ // This should only be used for the rolling quota system.
+ return;
+ }
+
+ if (!jobStatus.hasConnectivityConstraint()) {
+ return;
+ }
+
+ // Always check the full job readiness stat in case the component has been disabled.
+ if (wouldBeReadyWithConnectivityLocked(jobStatus)) {
+ if (DEBUG) {
+ Slog.i(TAG, "evaluateStateLocked finds job " + jobStatus + " would be ready.");
+ }
+ requestStandbyExceptionLocked(jobStatus);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "evaluateStateLocked finds job " + jobStatus + " would not be ready.");
+ }
+ maybeRevokeStandbyExceptionLocked(jobStatus);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Override
+ public void reevaluateStateLocked(final int uid) {
+ if (mConstants.USE_HEARTBEATS) {
+ return;
+ }
+ // Check if we still need a connectivity exception in case the JobService was disabled.
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(uid);
+ if (jobs == null) {
+ return;
+ }
+ for (int i = jobs.size() - 1; i >= 0; i--) {
+ evaluateStateLocked(jobs.valueAt(i));
+ }
+ }
+
+ /** Cancel the requested standby exception if none of the jobs would be ready to run anyway. */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ void maybeRevokeStandbyExceptionLocked(final JobStatus job) {
+ final int uid = job.getSourceUid();
+ if (!isStandbyExceptionRequestedLocked(uid)) {
+ return;
+ }
+ ArraySet<JobStatus> jobs = mRequestedWhitelistJobs.get(uid);
+ if (jobs == null) {
+ Slog.wtf(TAG,
+ "maybeRevokeStandbyExceptionLocked found null jobs array even though a "
+ + "standby exception has been requested.");
+ return;
+ }
+ if (!jobs.remove(job) || jobs.size() > 0) {
+ if (DEBUG) {
+ Slog.i(TAG,
+ "maybeRevokeStandbyExceptionLocked not revoking because there are still "
+ + jobs.size() + " jobs left.");
+ }
+ return;
+ }
+ // No more jobs that need an exception.
+ revokeStandbyExceptionLocked(uid);
+ }
+
+ /**
+ * Tell NetworkPolicyManager to revoke any exception it granted from its standby policy chain
+ * for the uid.
+ */
+ @GuardedBy("mLock")
+ private void revokeStandbyExceptionLocked(final int uid) {
+ if (DEBUG) Slog.i(TAG, "Revoking standby exception for UID: " + uid);
+ mNetPolicyManagerInternal.setAppIdleWhitelist(uid, false);
+ mRequestedWhitelistJobs.remove(uid);
+ }
+
/**
* Test to see if running the given job on the given network is insane.
* <p>
@@ -326,6 +510,14 @@
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
@Override
+ public void onAvailable(Network network) {
+ if (DEBUG) Slog.v(TAG, "onAvailable: " + network);
+ synchronized (mLock) {
+ mAvailableNetworks.add(network);
+ }
+ }
+
+ @Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) {
if (DEBUG) {
Slog.v(TAG, "onCapabilitiesChanged: " + network);
@@ -338,6 +530,9 @@
if (DEBUG) {
Slog.v(TAG, "onLost: " + network);
}
+ synchronized (mLock) {
+ mAvailableNetworks.remove(network);
+ }
updateTrackedJobs(-1, network);
}
};
@@ -356,6 +551,27 @@
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
+ if (mRequestedWhitelistJobs.size() > 0) {
+ pw.print("Requested standby exceptions:");
+ for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) {
+ pw.print(" ");
+ pw.print(mRequestedWhitelistJobs.keyAt(i));
+ pw.print(" (");
+ pw.print(mRequestedWhitelistJobs.valueAt(i).size());
+ pw.print(" jobs)");
+ }
+ pw.println();
+ }
+ if (mAvailableNetworks.size() > 0) {
+ pw.println("Available networks:");
+ pw.increaseIndent();
+ for (int i = 0; i < mAvailableNetworks.size(); i++) {
+ pw.println(mAvailableNetworks.valueAt(i));
+ }
+ pw.decreaseIndent();
+ } else {
+ pw.println("No available networks");
+ }
for (int i = 0; i < mTrackedJobs.size(); i++) {
final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
for (int j = 0; j < jobs.size(); j++) {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 4341589..82bfa51 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -1007,6 +1007,18 @@
* @return Whether or not this job is ready to run, based on its requirements.
*/
public boolean isReady() {
+ return isReady(mSatisfiedConstraintsOfInterest);
+ }
+
+ /**
+ * @return Whether or not this job would be ready to run if it had the specified constraint
+ * granted, based on its requirements.
+ */
+ public boolean wouldBeReadyWithConstraint(int constraint) {
+ return isReady(mSatisfiedConstraintsOfInterest | constraint);
+ }
+
+ private boolean isReady(int satisfiedConstraints) {
// Quota constraints trumps all other constraints.
if (!mReadyWithinQuota) {
return false;
@@ -1017,7 +1029,7 @@
// DeviceNotDozing implicit constraint must be satisfied
// NotRestrictedInBackground implicit constraint must be satisfied
return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
- || isConstraintsSatisfied());
+ || isConstraintsSatisfied(satisfiedConstraints));
}
static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
@@ -1033,12 +1045,16 @@
* @return Whether the constraints set on this job are satisfied.
*/
public boolean isConstraintsSatisfied() {
+ return isConstraintsSatisfied(mSatisfiedConstraintsOfInterest);
+ }
+
+ private boolean isConstraintsSatisfied(int satisfiedConstraints) {
if (overrideState == OVERRIDE_FULL) {
// force override: the job is always runnable
return true;
}
- int sat = mSatisfiedConstraintsOfInterest;
+ int sat = satisfiedConstraints;
if (overrideState == OVERRIDE_SOFT) {
// override: pretend all 'soft' requirements are satisfied
sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index b439c0d..61dc479 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -16,7 +16,10 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.DEBUG;
+
import android.content.Context;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.IndentingPrintWriter;
@@ -32,6 +35,8 @@
* are ready to run, or whether they must be stopped.
*/
public abstract class StateController {
+ private static final String TAG = "JobScheduler.SC";
+
protected final JobSchedulerService mService;
protected final StateChangedListener mStateChangedListener;
protected final Context mContext;
@@ -78,6 +83,37 @@
public void onConstantsUpdatedLocked() {
}
+ protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) {
+ // This is very cheap to check (just a few conditions on data in JobStatus).
+ final boolean jobWouldBeReady = jobStatus.wouldBeReadyWithConstraint(constraint);
+ if (DEBUG) {
+ Slog.v(TAG, "wouldBeReadyWithConstraintLocked: " + jobStatus.toShortString()
+ + " readyWithConstraint=" + jobWouldBeReady);
+ }
+ if (!jobWouldBeReady) {
+ // If the job wouldn't be ready, nothing to do here.
+ return false;
+ }
+
+ // This is potentially more expensive since JSS may have to query component
+ // presence.
+ return mService.areComponentsInPlaceLocked(jobStatus);
+ }
+
+ /**
+ * Called when JobSchedulerService has determined that the job is not ready to be run. The
+ * Controller can evaluate if it can or should do something to promote this job's readiness.
+ */
+ public void evaluateStateLocked(JobStatus jobStatus) {
+ }
+
+ /**
+ * Called when something with the UID has changed. The controller should re-evaluate any
+ * internal state tracking dependent on this UID.
+ */
+ public void reevaluateStateLocked(int uid) {
+ }
+
public abstract void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate);
public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2d2f9e3..fc364c0 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3621,12 +3621,19 @@
&& mAssistants.isSameUser(token, r.getUserId())) {
applyAdjustment(r, adjustment);
r.applyAdjustments();
+ // importance is checked at the beginning of the
+ // PostNotificationRunnable, before the signal extractors are run, so
+ // calculate the final importance here
+ r.calculateImportance();
foundEnqueued = true;
break;
}
}
if (!foundEnqueued) {
// adjustment arrived too late to apply to enqueued; apply to posted
+ // However, since the notification is now posted and may have alerted,
+ // ignore any importance related adjustments
+ adjustment.getSignals().remove(Adjustment.KEY_IMPORTANCE);
applyAdjustmentFromAssistant(token, adjustment);
}
}
@@ -3656,8 +3663,12 @@
NotificationRecord r = mNotificationsByKey.get(adjustment.getKey());
if (r != null && mAssistants.isSameUser(token, r.getUserId())) {
applyAdjustment(r, adjustment);
- r.applyImportanceFromAdjustments();
- if (r.getImportance() == IMPORTANCE_NONE) {
+ // If the assistant has blocked the notification, cancel it
+ // This will trigger a sort, so we don't have to explicitly ask for
+ // one here.
+ if (adjustment.getSignals().containsKey(Adjustment.KEY_IMPORTANCE)
+ && adjustment.getSignals().getInt(Adjustment.KEY_IMPORTANCE)
+ == IMPORTANCE_NONE) {
cancelNotificationsFromListener(token, new String[]{r.getKey()});
} else {
needsSort = true;
@@ -4758,7 +4769,6 @@
enqueueStatus);
}
- mRankingHelper.extractSignals(r);
// tell the assistant service about the notification
if (mAssistants.isEnabled()) {
mAssistants.onNotificationEnqueuedLocked(r);
@@ -4842,7 +4852,7 @@
| Notification.FLAG_NO_CLEAR;
}
- applyZenModeLocked(r);
+ mRankingHelper.extractSignals(r);
mRankingHelper.sort(mNotificationList);
if (!r.isHidden()) {
@@ -5627,6 +5637,7 @@
ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
ArrayList<ArrayList<Notification.Action>> systemSmartActionsBefore = new ArrayList<>(N);
ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
+ int[] importancesBefore = new int[N];
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
orderBefore.add(r.getKey());
@@ -5640,6 +5651,7 @@
suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
systemSmartActionsBefore.add(r.getSystemGeneratedSmartActions());
smartRepliesBefore.add(r.getSmartReplies());
+ importancesBefore[i] = r.getImportance();
mRankingHelper.extractSignals(r);
}
mRankingHelper.sort(mNotificationList);
@@ -5657,7 +5669,8 @@
r.getSuppressedVisualEffects())
|| !Objects.equals(systemSmartActionsBefore.get(i),
r.getSystemGeneratedSmartActions())
- || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())) {
+ || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())
+ || importancesBefore[i] != r.getImportance()) {
mHandler.scheduleSendRankingUpdate();
return;
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1a9257c..50810cc 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -21,10 +21,8 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEUTRAL;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_POSITIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -609,6 +607,17 @@
mIsAppImportanceLocked, this.sbn.getNotification());
}
+ public boolean hasAdjustment(String key) {
+ synchronized (mAdjustments) {
+ for (Adjustment adjustment : mAdjustments) {
+ if (adjustment.getSignals().containsKey(key)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public void addAdjustment(Adjustment adjustment) {
synchronized (mAdjustments) {
mAdjustments.add(adjustment);
@@ -669,18 +678,6 @@
.addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_REPLIES,
getSmartReplies().size()));
}
- }
- applyImportanceFromAdjustments();
- }
- }
-
- /**
- * Update importance from the adjustment.
- */
- public void applyImportanceFromAdjustments() {
- synchronized (mAdjustments) {
- for (Adjustment adjustment : mAdjustments) {
- Bundle signals = adjustment.getSignals();
if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
@@ -752,6 +749,8 @@
*/
public void setSystemImportance(int importance) {
mSystemImportance = importance;
+ // System importance is only changed in enqueue, so it's ok for us to calculate the
+ // importance directly instead of waiting for signal extractor.
calculateImportance();
}
@@ -762,7 +761,16 @@
*/
public void setAssistantImportance(int importance) {
mAssistantImportance = importance;
- calculateImportance();
+ // Unlike the system importance, the assistant importance can change on posted
+ // notifications, so don't calculateImportance() here, but wait for the signal extractors.
+ }
+
+ /**
+ * Returns the importance set by the assistant, or IMPORTANCE_UNSPECIFIED if the assistant
+ * hasn't set it.
+ */
+ public int getAssistantImportance() {
+ return mAssistantImportance;
}
/**
@@ -774,7 +782,8 @@
if (getChannel().hasUserSetImportance()) {
mImportanceExplanation = "user";
}
- if (!getChannel().hasUserSetImportance() && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
+ if (!getChannel().hasUserSetImportance()
+ && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
mImportance = mAssistantImportance;
mImportanceExplanation = "asst";
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2e9a71a..a95e730 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -27,7 +27,6 @@
import android.app.NotificationManager;
import android.app.PackageDeleteObserver;
import android.app.PackageInstallObserver;
-import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
import android.content.Intent;
@@ -164,6 +163,11 @@
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
+ // STOPSHIP: This is a temporary mock implementation of staged sessions. This variable
+ // shouldn't be needed at all.
+ @GuardedBy("mStagedSessions")
+ private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
+
/** Historical sessions kept around for debugging purposes */
@GuardedBy("mSessions")
private final List<String> mHistoricalSessions = new ArrayList<>();
@@ -481,10 +485,12 @@
if (!PackageHelper.fitsOnInternal(mContext, params)) {
throw new IOException("No suitable internal storage available");
}
- } else {
+ } 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.installFlags |= PackageManager.INSTALL_INTERNAL;
+ } else {
+ params.installFlags |= PackageManager.INSTALL_INTERNAL;
// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
@@ -536,6 +542,11 @@
synchronized (mSessions) {
mSessions.put(sessionId, session);
}
+ if (params.isStaged) {
+ synchronized (mStagedSessions) {
+ mStagedSessions.put(sessionId, session);
+ }
+ }
mCallbacks.notifySessionCreated(session.sessionId, session.userId);
writeSessionsAsync();
@@ -666,6 +677,18 @@
}
@Override
+ public ParceledListSlice<SessionInfo> getStagedSessions() {
+ final List<SessionInfo> result = new ArrayList<>();
+ synchronized (mStagedSessions) {
+ for (int i = 0; i < mStagedSessions.size(); i++) {
+ final PackageInstallerSession session = mStagedSessions.valueAt(i);
+ result.add(session.generateInfo(false));
+ }
+ }
+ return new ParceledListSlice<>(result);
+ }
+
+ @Override
public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
mPermissionManager.enforceCrossUserPermission(
Binder.getCallingUid(), userId, true, false, "getAllSessions");
@@ -714,22 +737,19 @@
// Check whether the caller is device owner or affiliated profile owner, in which case we do
// it silently.
- final int callingUserId = UserHandle.getUserId(callingUid);
DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
- final boolean isDeviceOwnerOrAffiliatedProfileOwner =
- dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)
- && dpmi.isUserAffiliatedWithDevice(callingUserId);
+ final boolean canSilentlyInstallPackage =
+ dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid);
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, versionedPackage.getPackageName(),
- isDeviceOwnerOrAffiliatedProfileOwner, userId);
+ canSilentlyInstallPackage, userId);
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
// Sweet, call straight through!
mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
- } else if (isDeviceOwnerOrAffiliatedProfileOwner) {
+ } else if (canSilentlyInstallPackage) {
// Allow the device owner and affiliated profile owner to silently delete packages
// Need to clear the calling identity to get DELETE_PACKAGES permission
long ident = Binder.clearCallingIdentity();
@@ -1110,6 +1130,18 @@
mInstallHandler.post(new Runnable() {
@Override
public void run() {
+ // TODO: remove this mock implementation.
+ if (session.isStaged()) {
+ // If the session is aborted, don't keep it in memory. Only store
+ // sessions successfully staged.
+ if (!success) {
+ synchronized (mStagedSessions) {
+ mStagedSessions.remove(session.sessionId);
+ }
+ } else {
+ return;
+ }
+ }
synchronized (mSessions) {
mSessions.remove(session.sessionId);
addHistoricalSessionLocked(session);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ea190a7..206a88b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -44,7 +44,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.apex.IApexService;
-import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -125,7 +124,7 @@
import java.util.concurrent.atomic.AtomicInteger;
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
- private static final String TAG = "PackageInstaller";
+ private static final String TAG = "PackageInstallerSession";
private static final boolean LOGD = true;
private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
@@ -147,6 +146,7 @@
private static final String ATTR_SEALED = "sealed";
private static final String ATTR_MULTI_PACKAGE = "multiPackage";
private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
+ private static final String ATTR_STAGED_SESSION = "stagedSession";
private static final String ATTR_MODE = "mode";
private static final String ATTR_INSTALL_FLAGS = "installFlags";
private static final String ATTR_INSTALL_LOCATION = "installLocation";
@@ -193,7 +193,7 @@
/** Package of the owner of the installer session */
@GuardedBy("mLock")
- private String mInstallerPackageName;
+ private @Nullable String mInstallerPackageName;
/** Uid of the owner of the installer session */
@GuardedBy("mLock")
@@ -339,11 +339,12 @@
*/
@GuardedBy("mLock")
private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
+ if (userId != UserHandle.getUserId(mInstallerUid)) {
+ return false;
+ }
DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
- return dpmi != null && dpmi.isActiveAdminWithPolicy(mInstallerUid,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) && dpmi.isUserAffiliatedWithDevice(
- userId);
+ return dpmi != null && dpmi.canSilentlyInstallPackage(mInstallerPackageName, mInstallerUid);
}
/**
@@ -463,6 +464,7 @@
info.grantedRuntimePermissions = params.grantedRuntimePermissions;
info.installFlags = params.installFlags;
info.isMultiPackage = params.isMultiPackage;
+ info.isStaged = params.isStaged;
info.parentSessionId = mParentSessionId;
info.childSessionIds = mChildSessionIds.copyKeys();
if (info.childSessionIds == null) {
@@ -1047,6 +1049,11 @@
if (committingSession == null) {
return;
}
+ if (isStaged()) {
+ // STOPSHIP: implement staged sessions
+ dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
+ return;
+ }
if (isMultiPackage()) {
final int[] childSessionIds = getChildSessionIds();
List<PackageManagerService.ActiveInstallSession> childSessions =
@@ -1816,6 +1823,11 @@
}
@Override
+ public boolean isStaged() {
+ return params.isStaged;
+ }
+
+ @Override
public int[] getChildSessionIds() {
final int[] childSessionIds = mChildSessionIds.copyKeys();
if (childSessionIds != null) {
@@ -1838,6 +1850,8 @@
return;
}
session.setParentSessionId(this.sessionId);
+ // TODO: sanity check, if parent session is staged then child session should be
+ // marked as staged.
addChildSessionIdInternal(sessionId);
}
}
@@ -1975,6 +1989,7 @@
pw.printPair("mFinalStatus", mFinalStatus);
pw.printPair("mFinalMessage", mFinalMessage);
pw.printPair("params.isMultiPackage", params.isMultiPackage);
+ pw.printPair("params.isStaged", params.isStaged);
pw.println();
pw.decreaseIndent();
@@ -2026,6 +2041,7 @@
writeBooleanAttribute(out, ATTR_SEALED, isSealed());
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
+ writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
// TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
// we've read all sessions.
writeIntAttribute(out, ATTR_PARENT_SESSION_ID, mParentSessionId);
@@ -2140,6 +2156,7 @@
final SessionParams params = new SessionParams(
SessionParams.MODE_INVALID);
params.isMultiPackage = readBooleanAttribute(in, ATTR_MULTI_PACKAGE, false);
+ params.isStaged = readBooleanAttribute(in, ATTR_STAGED_SESSION, false);
params.mode = readIntAttribute(in, ATTR_MODE);
params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fcdb84f..3e9100e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1298,6 +1298,7 @@
final @Nullable String mStorageManagerPackage;
final @Nullable String mSystemTextClassifierPackage;
final @Nullable String mWellbeingPackage;
+ final @Nullable String mDocumenterPackage;
final @NonNull String mServicesSystemSharedLibraryPackageName;
final @NonNull String mSharedSystemSharedLibraryPackageName;
@@ -2781,6 +2782,7 @@
mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
mWellbeingPackage = getWellbeingPackageName();
+ mDocumenterPackage = getDocumenterPackageName();
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
@@ -15267,9 +15269,12 @@
final DeletePackageAction deletePackageAction;
// we only want to try to delete for non system apps
if (prepareResult.replace && !prepareResult.system) {
+ final boolean killApp = (scanResult.request.scanFlags & SCAN_DONT_KILL_APP) == 0;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+ | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
deletePackageAction = mayDeletePackageLocked(res.removedInfo,
prepareResult.originalPs, prepareResult.disabledPs,
- prepareResult.childPackageSettings);
+ prepareResult.childPackageSettings, deleteFlags, installArgs.user);
if (deletePackageAction == null) {
throw new ReconcileFailure(
PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
@@ -15351,12 +15356,9 @@
}
}
} else {
- final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0;
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA
- | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
try {
executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
- null, true, request.mAllUsers, deleteFlags, true, pkg);
+ true, request.mAllUsers, true, pkg);
} catch (SystemDeleteException e) {
if (Build.IS_ENG) {
throw new RuntimeException("Unexpected failure", e);
@@ -17816,12 +17818,23 @@
public final PackageSetting deletingPs;
public final PackageSetting disabledPs;
public final PackageRemovedInfo outInfo;
+ public final int flags;
+ public final UserHandle user;
+ /**
+ * True if this package is an unupdated system app that may be deleted by the system.
+ * When true, disabledPs will be null.
+ */
+ public final boolean mayDeleteUnupdatedSystemApp;
private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
- PackageRemovedInfo outInfo) {
+ PackageRemovedInfo outInfo, int flags, UserHandle user,
+ boolean mayDeleteUnupdatedSystemApp) {
this.deletingPs = deletingPs;
this.disabledPs = disabledPs;
this.outInfo = outInfo;
+ this.flags = flags;
+ this.user = user;
+ this.mayDeleteUnupdatedSystemApp = mayDeleteUnupdatedSystemApp;
}
}
@@ -17833,23 +17846,26 @@
@GuardedBy("mPackages")
private static DeletePackageAction mayDeletePackageLocked(
PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
- @Nullable PackageSetting[] children) {
+ @Nullable PackageSetting[] children, int flags, UserHandle user) {
if (ps == null) {
return null;
}
+ boolean mayDeleteUnupdatedSystemApp = false;
if (isSystemApp(ps)) {
if (ps.parentPackageName != null) {
Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
return null;
}
- // 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);
+ if (((flags & PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
+ && user.getIdentifier() != UserHandle.USER_ALL) {
+ mayDeleteUnupdatedSystemApp = true;
+ } else if (disabledPs == null) {
+ // Confirmed 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
+ Slog.w(TAG, "Attempt to delete unknown system package " + ps.pkg.packageName);
return null;
}
}
@@ -17866,7 +17882,8 @@
}
}
}
- return new DeletePackageAction(ps, disabledPs, outInfo);
+ return new DeletePackageAction(ps, disabledPs, outInfo, flags, user,
+ mayDeleteUnupdatedSystemApp);
}
/*
@@ -17881,7 +17898,7 @@
final PackageSetting ps = mSettings.mPackages.get(packageName);
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
PackageSetting[] children = mSettings.getChildSettingsLPr(ps);
- action = mayDeletePackageLocked(outInfo, ps, disabledPs, children);
+ action = mayDeletePackageLocked(outInfo, ps, disabledPs, children, flags, user);
}
if (null == action) {
return false;
@@ -17890,8 +17907,8 @@
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
try {
- executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
- allUserHandles, flags, writeSettings, replacingPackage);
+ executeDeletePackageLIF(action, packageName, deleteCodeAndResources,
+ allUserHandles, writeSettings, replacingPackage);
} catch (SystemDeleteException e) {
return false;
}
@@ -17908,11 +17925,13 @@
/** 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, boolean writeSettings,
+ String packageName, boolean deleteCodeAndResources,
+ int[] allUserHandles, boolean writeSettings,
PackageParser.Package replacingPackage) throws SystemDeleteException {
final PackageSetting ps = action.deletingPs;
final PackageRemovedInfo outInfo = action.outInfo;
+ final UserHandle user = action.user;
+ final int flags = action.flags;
final boolean systemApp = isSystemApp(ps);
synchronized (mPackages) {
@@ -17938,8 +17957,7 @@
}
- if (((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
- && user.getIdentifier() != UserHandle.USER_ALL)) {
+ if (!systemApp || action.mayDeleteUnupdatedSystemApp) {
// The caller is asking that the package only be deleted for a single
// user. To do this, we just mark its uninstalled state and delete
// its data. If this is a system app, we only allow this to happen if
@@ -19567,6 +19585,22 @@
return mContext.getString(R.string.config_defaultTextClassifierPackage);
}
+ private @Nullable String getDocumenterPackageName() {
+ final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+
+ final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+ MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
+ | MATCH_DISABLED_COMPONENTS,
+ UserHandle.myUserId());
+ if (matches.size() == 1) {
+ return matches.get(0).getComponentInfo().packageName;
+ } else {
+ Slog.e(TAG, "There should probably be exactly one documenter; found "
+ + matches.size() + ": matches=" + matches);
+ return null;
+ }
+ }
+
@Override
public String getWellbeingPackageName() {
return mContext.getString(R.string.config_defaultWellbeingPackage);
@@ -22730,6 +22764,8 @@
return mRequiredPermissionControllerPackage;
case PackageManagerInternal.PACKAGE_WELLBEING:
return mWellbeingPackage;
+ case PackageManagerInternal.PACKAGE_DOCUMENTER:
+ return mDocumenterPackage;
}
return null;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 77f8c3a..37a35a2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -536,6 +536,7 @@
boolean listInstaller = false;
boolean showUid = false;
boolean showVersionCode = false;
+ boolean listApexOnly = false;
int uid = -1;
int userId = UserHandle.USER_SYSTEM;
try {
@@ -576,6 +577,10 @@
case "--show-versioncode":
showVersionCode = true;
break;
+ case "--apex-only":
+ getFlags |= PackageManager.MATCH_APEX;
+ listApexOnly = true;
+ break;
case "--user":
userId = UserHandle.parseUserArg(getNextArgRequired());
break;
@@ -606,30 +611,34 @@
if (filter != null && !info.packageName.contains(filter)) {
continue;
}
- if (uid != -1 && info.applicationInfo.uid != uid) {
+ final boolean isApex = info.isApex;
+ if (uid != -1 && !isApex && info.applicationInfo.uid != uid) {
continue;
}
- final boolean isSystem =
+
+ final boolean isSystem = !isApex &&
(info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
- if ((!listDisabled || !info.applicationInfo.enabled) &&
- (!listEnabled || info.applicationInfo.enabled) &&
+ final boolean isEnabled = !isApex && info.applicationInfo.enabled;
+ if ((!listDisabled || !isEnabled) &&
+ (!listEnabled || isEnabled) &&
(!listSystem || isSystem) &&
- (!listThirdParty || !isSystem)) {
+ (!listThirdParty || !isSystem) &&
+ (!listApexOnly || isApex)) {
pw.print("package:");
- if (showSourceDir) {
+ if (showSourceDir && !isApex) {
pw.print(info.applicationInfo.sourceDir);
pw.print("=");
}
pw.print(info.packageName);
- if (showVersionCode) {
+ if (showVersionCode && !isApex) {
pw.print(" versionCode:");
pw.print(info.applicationInfo.versionCode);
}
- if (listInstaller) {
+ if (listInstaller && !isApex) {
pw.print(" installer=");
pw.print(mInterface.getInstallerPackageName(info.packageName));
}
- if (showUid) {
+ if (showUid && !isApex) {
pw.print(" uid:");
pw.print(info.applicationInfo.uid);
}
@@ -2284,6 +2293,9 @@
case "--multi-package":
sessionParams.setMultiPackage();
break;
+ case "--staged":
+ sessionParams.setStaged();
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -2767,11 +2779,11 @@
pw.println(" Prints all system libraries.");
pw.println("");
pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] ");
- pw.println(" [--uid UID] [--user USER_ID] [FILTER]");
+ pw.println(" [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
pw.println(" Prints all packages; optionally only those whose name contains");
pw.println(" the text in FILTER. Options are:");
pw.println(" -f: see their associated file");
- pw.println(" -a: all known packages");
+ pw.println(" -a: all known packages (but excluding APEXes)");
pw.println(" -d: filter to only show disabled packages");
pw.println(" -e: filter to only show enabled packages");
pw.println(" -s: filter to only show system packages");
@@ -2780,6 +2792,7 @@
pw.println(" -l: ignored (used for compatibility with older releases)");
pw.println(" -U: also show the package UID");
pw.println(" -u: also include uninstalled packages");
+ pw.println(" --apex-only: only show APEX packages");
pw.println(" --uid UID: filter to only show packages with the given UID");
pw.println(" --user USER_ID: only list packages belonging to the given user");
pw.println("");
@@ -2853,7 +2866,7 @@
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]");
- pw.println(" [--multi-package]");
+ pw.println(" [--multi-package] [--staged]");
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.");
pw.println("");
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 59f67ff..3a49412 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -244,6 +244,9 @@
public boolean isWellbeing() {
return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
}
+ public boolean isDocumenter() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
+ }
public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index bc3c18d..31f5ce4 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1647,6 +1647,13 @@
// Special permission granted only to the OEM specified wellbeing app
allowed = true;
}
+ if (!allowed && bp.isDocumenter()
+ && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM))) {
+ // If this permission is to be granted to the documenter and
+ // this app is the documenter, then it gets the permission.
+ allowed = true;
+ }
}
return allowed;
}
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index c5139b5..cedb548 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -111,7 +111,7 @@
false, /* enableAdjustBrightness */
false, /* enableDataSaver */
true, /* enableFirewall */
- false, /* enableQuickDoze */
+ true, /* enableQuickDoze */
new ArrayMap<>(), /* filesForInteractive */
new ArrayMap<>(), /* filesForNoninteractive */
true, /* forceAllAppsStandby */
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 2eea3a4..02689a9 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -453,14 +453,15 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
+ pw.println("IsStatusOverride: " + mIsStatusOverride);
pw.println("ThermalEventListeners:");
mThermalEventListeners.dump(pw, "\t");
pw.println("ThermalStatusListeners:");
mThermalStatusListeners.dump(pw, "\t");
- pw.println("Thermal Status: " + Integer.toString(mStatus));
+ pw.println("Thermal Status: " + mStatus);
pw.println("Cached temperatures:");
dumpTemperaturesLocked(pw, "\t", mTemperatureMap.values());
- pw.println("HAL Ready: " + Boolean.toString(mHalReady));
+ pw.println("HAL Ready: " + mHalReady);
if (mHalReady) {
pw.println("HAL connection:");
mHalWrapper.dump(pw, "\t");
@@ -532,7 +533,7 @@
return -1;
}
if (!Temperature.isValidStatus(status)) {
- pw.println("Invalid status: " + Integer.toString(status));
+ pw.println("Invalid status: " + status);
return -1;
}
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 8711ddf..35013de 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -22,8 +22,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManager;
import android.app.role.IRoleManagerCallback;
import android.app.role.RoleManager;
@@ -33,6 +35,9 @@
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
+import android.os.Handler;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
@@ -53,6 +58,8 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -73,7 +80,7 @@
*
* @see RoleManager
*/
-public class RoleManagerService extends SystemService {
+public class RoleManagerService extends SystemService implements RoleUserState.Callback {
private static final String LOG_TAG = RoleManagerService.class.getSimpleName();
@@ -100,6 +107,17 @@
private final SparseArray<RemoteRoleControllerService> mControllerServices =
new SparseArray<>();
+ /**
+ * Maps user id to its list of listeners.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<RemoteCallbackList<IOnRoleHoldersChangedListener>> mListeners =
+ new SparseArray<>();
+
+ @NonNull
+ private final Handler mListenerHandler = FgThread.getHandler();
+
public RoleManagerService(@NonNull Context context) {
super(context);
@@ -188,7 +206,7 @@
}
@Nullable
- private String computeComponentStateHash(@UserIdInt int userId) {
+ private static String computeComponentStateHash(@UserIdInt int userId) {
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -223,7 +241,7 @@
synchronized (mLock) {
RoleUserState userState = mUserStates.get(userId);
if (userState == null) {
- userState = new RoleUserState(userId);
+ userState = new RoleUserState(userId, this);
mUserStates.put(userId, userState);
}
return userState;
@@ -242,17 +260,70 @@
}
}
+ @Nullable
+ private RemoteCallbackList<IOnRoleHoldersChangedListener> getListeners(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return mListeners.get(userId);
+ }
+ }
+
+ @NonNull
+ private RemoteCallbackList<IOnRoleHoldersChangedListener> getOrCreateListeners(
+ @UserIdInt int userId) {
+ synchronized (mLock) {
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = mListeners.get(userId);
+ if (listeners == null) {
+ listeners = new RemoteCallbackList<>();
+ mListeners.put(userId, listeners);
+ }
+ return listeners;
+ }
+ }
+
private void onRemoveUser(@UserIdInt int userId) {
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners;
RoleUserState userState;
synchronized (mLock) {
+ listeners = mListeners.removeReturnOld(userId);
mControllerServices.remove(userId);
userState = mUserStates.removeReturnOld(userId);
}
+ if (listeners != null) {
+ listeners.kill();
+ }
if (userState != null) {
userState.destroy();
}
}
+ @Override
+ public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ mListenerHandler.sendMessage(PooledLambda.obtainMessage(
+ RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId));
+ }
+
+ @WorkerThread
+ private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
+ if (listeners == null) {
+ return;
+ }
+
+ int broadcastCount = listeners.beginBroadcast();
+ try {
+ for (int i = 0; i < broadcastCount; i++) {
+ IOnRoleHoldersChangedListener listener = listeners.getBroadcastItem(i);
+ try {
+ listener.onRoleHoldersChanged(roleName, userId);
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Error calling OnRoleHoldersChangedListener", e);
+ }
+ }
+ } finally {
+ listeners.finishBroadcast();
+ }
+ }
+
private class Stub extends IRoleManager.Stub {
@Override
@@ -357,6 +428,42 @@
}
@Override
+ public void addOnRoleHoldersChangedListenerAsUser(
+ @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ if (!mUserManagerInternal.exists(userId)) {
+ Slog.e(LOG_TAG, "user " + userId + " does not exist");
+ return;
+ }
+ userId = handleIncomingUser(userId, "addOnRoleHoldersChangedListenerAsUser");
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
+ "addOnRoleHoldersChangedListenerAsUser");
+
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getOrCreateListeners(
+ userId);
+ listeners.register(listener);
+ }
+
+ @Override
+ public void removeOnRoleHoldersChangedListenerAsUser(
+ @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ if (!mUserManagerInternal.exists(userId)) {
+ Slog.e(LOG_TAG, "user " + userId + " does not exist");
+ return;
+ }
+ userId = handleIncomingUser(userId, "removeOnRoleHoldersChangedListenerAsUser");
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
+ "removeOnRoleHoldersChangedListenerAsUser");
+
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
+ if (listener == null) {
+ return;
+ }
+ listeners.unregister(listener);
+ }
+
+ @Override
public void setRoleNamesFromController(@NonNull List<String> roleNames) {
Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
getContext().enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index ec614a4..d55e261 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -74,6 +74,9 @@
private final int mUserId;
@NonNull
+ private final Callback mCallback;
+
+ @NonNull
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -100,12 +103,14 @@
private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
/**
- * Create a new instance of user state, and read its state from disk if previously persisted.
+ * Create a new user state, and read its state from disk if previously persisted.
*
- * @param userId the user id for the new user state
+ * @param userId the user id for this user state
+ * @param callback the callback for this user state
*/
- public RoleUserState(@UserIdInt int userId) {
+ public RoleUserState(@UserIdInt int userId, @NonNull Callback callback) {
mUserId = userId;
+ mCallback = callback;
readFile();
}
@@ -116,6 +121,7 @@
public int getVersion() {
synchronized (mLock) {
throwIfDestroyedLocked();
+
return mVersion;
}
}
@@ -128,6 +134,7 @@
public void setVersion(int version) {
synchronized (mLock) {
throwIfDestroyedLocked();
+
if (mVersion == version) {
return;
}
@@ -156,6 +163,7 @@
public void setPackagesHash(@Nullable String packagesHash) {
synchronized (mLock) {
throwIfDestroyedLocked();
+
if (Objects.equals(mPackagesHash, packagesHash)) {
return;
}
@@ -174,6 +182,7 @@
public boolean isRoleAvailable(@NonNull String roleName) {
synchronized (mLock) {
throwIfDestroyedLocked();
+
return mRoles.containsKey(roleName);
}
}
@@ -189,6 +198,7 @@
public ArraySet<String> getRoleHolders(@NonNull String roleName) {
synchronized (mLock) {
throwIfDestroyedLocked();
+
return new ArraySet<>(mRoles.get(roleName));
}
}
@@ -201,29 +211,34 @@
public void setRoleNames(@NonNull List<String> roleNames) {
synchronized (mLock) {
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);
+ 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) {
scheduleWriteFileLocked();
}
@@ -241,20 +256,27 @@
*/
@CheckResult
public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
+ boolean changed;
+
synchronized (mLock) {
throwIfDestroyedLocked();
+
ArraySet<String> roleHolders = mRoles.get(roleName);
if (roleHolders == null) {
Slog.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
+ ", package: " + packageName);
return false;
}
- boolean changed = roleHolders.add(packageName);
+ changed = roleHolders.add(packageName);
if (changed) {
scheduleWriteFileLocked();
}
- return true;
}
+
+ if (changed) {
+ mCallback.onRoleHoldersChanged(roleName, mUserId);
+ }
+ return true;
}
/**
@@ -268,20 +290,28 @@
*/
@CheckResult
public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
+ boolean changed;
+
synchronized (mLock) {
throwIfDestroyedLocked();
+
ArraySet<String> roleHolders = mRoles.get(roleName);
if (roleHolders == null) {
Slog.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
+ ", package: " + packageName);
return false;
}
- boolean changed = roleHolders.remove(packageName);
+
+ changed = roleHolders.remove(packageName);
if (changed) {
scheduleWriteFileLocked();
}
- return true;
}
+
+ if (changed) {
+ mCallback.onRoleHoldersChanged(roleName, mUserId);
+ }
+ return true;
}
/**
@@ -520,8 +550,8 @@
}
/**
- * Destroy this state and delete the corresponding file. Any pending writes to the file will be
- * cancelled and any future interaction with this state will throw an exception.
+ * Destroy this user state and delete the corresponding file. Any pending writes to the file
+ * will be cancelled, and any future interaction with this state will throw an exception.
*/
public void destroy() {
synchronized (mLock) {
@@ -542,4 +572,18 @@
private static @NonNull File getFile(@UserIdInt int userId) {
return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME);
}
+
+ /**
+ * Callback for a user state.
+ */
+ public interface Callback {
+
+ /**
+ * Called when the holders of roles are changed.
+ *
+ * @param roleName the name of the role whose holders are changed
+ * @param userId the user id for this role holder change
+ */
+ void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId);
+ }
}
diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
new file mode 100644
index 0000000..944db84
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.signedconfig;
+
+import android.os.Build;
+import android.util.Slog;
+
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+/**
+ * Helper class for verifying config signatures.
+ */
+public class SignatureVerifier {
+
+ private static final String TAG = "SignedConfig";
+ private static final boolean DBG = false;
+
+ private static final String DEBUG_KEY =
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60"
+ + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==";
+
+ private final PublicKey mDebugKey;
+
+ public SignatureVerifier() {
+ mDebugKey = createKey(DEBUG_KEY);
+ }
+
+ private static PublicKey createKey(String base64) {
+ EncodedKeySpec keySpec;
+ try {
+ byte[] key = Base64.getDecoder().decode(base64);
+ keySpec = new X509EncodedKeySpec(key);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Failed to base64 decode public key", e);
+ return null;
+ }
+ try {
+ KeyFactory factory = KeyFactory.getInstance("EC");
+ return factory.generatePublic(keySpec);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ Slog.e(TAG, "Failed to construct public key", e);
+ return null;
+ }
+ }
+
+ /**
+ * Verify a signature for signed config.
+ *
+ * @param config Config as read from APK meta-data.
+ * @param base64Signature Signature as read from APK meta-data.
+ * @return {@code true} iff the signature was successfully verified.
+ */
+ public boolean verifySignature(String config, String base64Signature)
+ throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ byte[] signature;
+ try {
+ signature = Base64.getDecoder().decode(base64Signature);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Failed to base64 decode signature");
+ return false;
+ }
+ byte[] data = config.getBytes(StandardCharsets.UTF_8);
+ if (DBG) Slog.i(TAG, "Data: " + Base64.getEncoder().encodeToString(data));
+
+ if (Build.IS_DEBUGGABLE) {
+ if (mDebugKey != null) {
+ if (DBG) Slog.w(TAG, "Trying to verify signature using debug key");
+ Signature verifier = Signature.getInstance("SHA256withECDSA");
+ verifier.initVerify(mDebugKey);
+ verifier.update(data);
+ if (verifier.verify(signature)) {
+ Slog.i(TAG, "Verified config using debug key");
+ return true;
+ } else {
+ if (DBG) Slog.i(TAG, "Config verification failed using debug key");
+ }
+ } else {
+ Slog.w(TAG, "Debuggable build, but have no debug key");
+ }
+ }
+ // TODO verify production key.
+ Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION");
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfig.java b/services/core/java/com/android/server/signedconfig/SignedConfig.java
index e6bb800..560a1e1 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfig.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfig.java
@@ -33,19 +33,34 @@
* Represents signed configuration.
*
* <p>This configuration should only be used if the signature has already been verified.
+ *
+ * This class also parses signed config from JSON. The format expected is:
+ * <pre>
+ * {
+ * "version": 1
+ * "config": [
+ * {
+ * "min_sdk": 28,
+ * "max_sdk": 29,
+ * "values": {
+ * "key": "value",
+ * "key2": "value2"
+ * ...
+ * }
+ * },
+ * ...
+ * ],
+ * }
+ * </pre>
*/
public class SignedConfig {
private static final String KEY_VERSION = "version";
private static final String KEY_CONFIG = "config";
- private static final String CONFIG_KEY_MIN_SDK = "minSdk";
- private static final String CONFIG_KEY_MAX_SDK = "maxSdk";
+ private static final String CONFIG_KEY_MIN_SDK = "min_sdk";
+ private static final String CONFIG_KEY_MAX_SDK = "max_sdk";
private static final String CONFIG_KEY_VALUES = "values";
- // TODO it may be better to use regular key/value pairs in a JSON object, rather than an array
- // of objects with the 2 keys below.
- private static final String CONFIG_KEY_KEY = "key";
- private static final String CONFIG_KEY_VALUE = "value";
/**
* Represents config values targeting an SDK range.
@@ -141,14 +156,10 @@
throws JSONException, InvalidConfigException {
int minSdk = json.getInt(CONFIG_KEY_MIN_SDK);
int maxSdk = json.getInt(CONFIG_KEY_MAX_SDK);
- JSONArray valueArray = json.getJSONArray(CONFIG_KEY_VALUES);
+ JSONObject valuesJson = json.getJSONObject(CONFIG_KEY_VALUES);
Map<String, String> values = new HashMap<>();
- for (int i = 0; i < valueArray.length(); ++i) {
- JSONObject keyValuePair = valueArray.getJSONObject(i);
- String key = keyValuePair.getString(CONFIG_KEY_KEY);
- Object valueObject = keyValuePair.has(CONFIG_KEY_VALUE)
- ? keyValuePair.get(CONFIG_KEY_VALUE)
- : null;
+ for (String key : valuesJson.keySet()) {
+ Object valueObject = valuesJson.get(key);
String value = valueObject == JSONObject.NULL || valueObject == null
? null
: valueObject.toString();
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
index e4d799a..4908964 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
@@ -24,6 +24,7 @@
import android.util.ArraySet;
import android.util.Slog;
+import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
@@ -65,15 +66,21 @@
private final Context mContext;
private final String mSourcePackage;
+ private final SignatureVerifier mVerifier;
SignedConfigApplicator(Context context, String sourcePackage) {
mContext = context;
mSourcePackage = sourcePackage;
+ mVerifier = new SignatureVerifier();
}
private boolean checkSignature(String data, String signature) {
- Slog.w(TAG, "SIGNATURE CHECK NOT IMPLEMENTED YET!");
- return false;
+ try {
+ return mVerifier.verifySignature(data, signature);
+ } catch (GeneralSecurityException e) {
+ Slog.e(TAG, "Failed to verify signature", e);
+ return false;
+ }
}
private int getCurrentConfigVersion() {
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
index be1d41d..84ce93f 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -29,6 +29,9 @@
import com.android.server.LocalServices;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
/**
* Signed config service. This is not an Android Service, but just owns a broadcast receiver for
* receiving package install and update notifications from the package manager.
@@ -81,6 +84,13 @@
&& metaData.containsKey(KEY_CONFIG_SIGNATURE)) {
String config = metaData.getString(KEY_CONFIG);
String signature = metaData.getString(KEY_CONFIG_SIGNATURE);
+ try {
+ // Base64 encoding is standard (not URL safe) encoding: RFC4648
+ config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
+ } catch (IllegalArgumentException iae) {
+ Slog.e(TAG, "Failed to base64 decode config from " + packageName);
+ return;
+ }
if (DBG) {
Slog.d(TAG, "Got signed config: " + config);
Slog.d(TAG, "Got config signature: " + signature);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4e9c5ab..608e450 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -96,6 +96,7 @@
import static com.android.server.am.ActivityRecordProto.VISIBLE;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_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;
@@ -122,8 +123,7 @@
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;
@@ -157,6 +157,7 @@
import android.app.servertransaction.PipModeChangeItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.WindowVisibilityItem;
+import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -716,8 +717,12 @@
// to forcing the update of the picture-in-picture mode as a part of the PiP animation.
mLastReportedPictureInPictureMode = inPictureInPictureMode;
mLastReportedMultiWindowMode = inPictureInPictureMode;
- final Configuration newConfig = task.computeNewOverrideConfigurationForBounds(
- targetStackBounds, null);
+ final Configuration newConfig = new Configuration();
+ if (targetStackBounds != null && !targetStackBounds.isEmpty()) {
+ task.computeResolvedOverrideConfiguration(newConfig,
+ task.getParent().getConfiguration(),
+ task.getRequestedOverrideConfiguration());
+ }
schedulePictureInPictureModeChanged(newConfig);
scheduleMultiWindowModeChanged(newConfig);
}
@@ -1035,8 +1040,6 @@
inHistory = true;
- final TaskWindowContainerController taskController = task.getWindowContainerController();
-
// TODO(b/36505427): Maybe this call should be moved inside updateOverrideConfiguration()
task.updateOverrideConfigurationFromLaunchBounds();
// Make sure override configuration is up-to-date before using to create window controller.
@@ -1048,10 +1051,9 @@
// TODO: Should this throw an exception instead?
Slog.w(TAG, "Attempted to add existing app token: " + appToken);
} else {
- final Task container = taskController.mContainer;
+ final Task container = task.getTask();
if (container == null) {
- throw new IllegalArgumentException("AppWindowContainerController: invalid "
- + " controller=" + taskController);
+ throw new IllegalArgumentException("createAppWindowToken: invalid task =" + task);
}
mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,
task.voiceSession != null, container.getDisplayContent(),
@@ -1062,7 +1064,7 @@
mLaunchTaskBehind, isAlwaysFocusable());
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) {
Slog.v(TAG, "addAppToken: "
- + mAppWindowToken + " controller=" + taskController + " at "
+ + mAppWindowToken + " task=" + container + " at "
+ Integer.MAX_VALUE);
}
container.addChild(mAppWindowToken, Integer.MAX_VALUE /* add on top */);
@@ -1091,6 +1093,12 @@
Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + appToken);
return false;
}
+ if (mAppWindowToken.getTask() == null) {
+ // Can be removed after unification of Task and TaskRecord.
+ Slog.w(TAG_WM, "Attempted to start a window to an app token not having attached to any"
+ + " task: " + appToken);
+ return false;
+ }
return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel,
labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch,
processRunning, allowTaskSnapshot, activityCreated, fromRecents);
@@ -1148,7 +1156,7 @@
+ " r=" + this + " (" + prevTask.getStackId() + ")");
}
- mAppWindowToken.reparent(newTask.getWindowContainerController(), position);
+ mAppWindowToken.reparent(newTask.getTask(), 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.
@@ -1801,6 +1809,18 @@
}
mAppWindowToken.detachChildren();
}
+
+ if (state == RESUMED) {
+ mAtmService.updateBatteryStats(this, true);
+ mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_RESUMED);
+ } else if (state == PAUSED) {
+ mAtmService.updateBatteryStats(this, false);
+ mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED);
+ } else if (state == STOPPED) {
+ mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED);
+ } else if (state == DESTROYED) {
+ mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED);
+ }
}
ActivityState getState() {
@@ -2542,12 +2562,10 @@
setBounds(mTmpBounds);
- final Rect updatedBounds = getRequestedOverrideBounds();
-
// Bounds changed...update configuration to match.
if (!matchParentBounds()) {
- task.computeOverrideConfiguration(mTmpConfig, updatedBounds,
- false /* overrideWidth */, false /* overrideHeight */);
+ task.computeResolvedOverrideConfiguration(mTmpConfig,
+ task.getParent().getConfiguration(), getRequestedOverrideConfiguration());
}
onRequestedOverrideConfigurationChanged(mTmpConfig);
@@ -2774,7 +2792,7 @@
final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
if (hasResizeChange) {
final boolean isDragResizing =
- getTaskRecord().getWindowContainerController().isDragResizing();
+ getTaskRecord().getTask().isDragResizing();
mRelaunchReason = isDragResizing ? RELAUNCH_REASON_FREE_RESIZE
: RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
} else {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index aca9702..2663d99 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -294,13 +294,6 @@
// stack and the new stack will be on top of all stacks.
static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2;
- // The height/width divide used when fitting a task within a bounds with method
- // {@link #fitWithinBounds}.
- // We always want the task to to be visible in the bounds without affecting its size when
- // fitting. To make sure this is the case, we don't adjust the task left or top side pass
- // the input bounds right or bottom side minus the width or height divided by this value.
- private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
-
final ActivityTaskManagerService mService;
private final WindowManagerService mWindowManager;
T mWindowContainerController;
@@ -365,9 +358,9 @@
private boolean mUpdateBoundsDeferred;
private boolean mUpdateBoundsDeferredCalled;
+ private boolean mUpdateDisplayedBoundsDeferredCalled;
private final Rect mDeferredBounds = new Rect();
- private final Rect mDeferredTaskBounds = new Rect();
- private final Rect mDeferredTaskInsetBounds = new Rect();
+ private final Rect mDeferredDisplayedBounds = new Rect();
int mCurrentUser;
@@ -605,7 +598,9 @@
if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
getStackDockedModeBounds(null, null, mTmpRect2, mTmpRect3);
// immediately resize so docked bounds are available in onSplitScreenModeActivated
- resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
+ setTaskDisplayedBounds(null);
+ setTaskBounds(mTmpRect2);
+ setBounds(mTmpRect2);
} else if (
getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
Rect dockedBounds = display.getSplitScreenPrimaryStack().getBounds();
@@ -911,7 +906,7 @@
}
void positionChildWindowContainerAtTop(TaskRecord child) {
- mWindowContainerController.positionChildAtTop(child.getWindowContainerController(),
+ mWindowContainerController.positionChildAtTop(child.getTask(),
true /* includingParents */);
}
@@ -921,7 +916,7 @@
// task to bottom, the next focusable stack on the same display should be focused.
final ActivityStack nextFocusableStack = getDisplay().getNextFocusableStack(
child.getStack(), true /* ignoreCurrent */);
- mWindowContainerController.positionChildAtBottom(child.getWindowContainerController(),
+ mWindowContainerController.positionChildAtBottom(child.getTask(),
nextFocusableStack == null /* includingParents */);
}
@@ -949,17 +944,19 @@
* be resized to that bounds.
*/
void continueUpdateBounds() {
- final boolean wasDeferred = mUpdateBoundsDeferred;
- mUpdateBoundsDeferred = false;
- if (wasDeferred && mUpdateBoundsDeferredCalled) {
- resize(mDeferredBounds.isEmpty() ? null : mDeferredBounds,
- mDeferredTaskBounds.isEmpty() ? null : mDeferredTaskBounds,
- mDeferredTaskInsetBounds.isEmpty() ? null : mDeferredTaskInsetBounds);
+ if (mUpdateBoundsDeferred) {
+ mUpdateBoundsDeferred = false;
+ if (mUpdateBoundsDeferredCalled) {
+ setTaskBounds(mDeferredBounds);
+ setBounds(mDeferredBounds);
+ }
+ if (mUpdateDisplayedBoundsDeferredCalled) {
+ setTaskDisplayedBounds(mDeferredDisplayedBounds);
+ }
}
}
- boolean updateBoundsAllowed(Rect bounds, Rect tempTaskBounds,
- Rect tempTaskInsetBounds) {
+ boolean updateBoundsAllowed(Rect bounds) {
if (!mUpdateBoundsDeferred) {
return true;
}
@@ -968,20 +965,23 @@
} else {
mDeferredBounds.setEmpty();
}
- if (tempTaskBounds != null) {
- mDeferredTaskBounds.set(tempTaskBounds);
- } else {
- mDeferredTaskBounds.setEmpty();
- }
- if (tempTaskInsetBounds != null) {
- mDeferredTaskInsetBounds.set(tempTaskInsetBounds);
- } else {
- mDeferredTaskInsetBounds.setEmpty();
- }
mUpdateBoundsDeferredCalled = true;
return false;
}
+ boolean updateDisplayedBoundsAllowed(Rect bounds) {
+ if (!mUpdateBoundsDeferred) {
+ return true;
+ }
+ if (bounds != null) {
+ mDeferredDisplayedBounds.set(bounds);
+ } else {
+ mDeferredDisplayedBounds.setEmpty();
+ }
+ mUpdateDisplayedBoundsDeferredCalled = true;
+ return false;
+ }
+
@Override
public int setBounds(Rect bounds) {
return super.setBounds(!inMultiWindowMode() ? null : bounds);
@@ -1617,7 +1617,6 @@
try {
EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
- mService.updateUsageStats(prev, false);
mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
@@ -2984,7 +2983,7 @@
position = getAdjustedPositionForTask(task, position, null /* starting */);
mTaskHistory.remove(task);
mTaskHistory.add(position, task);
- mWindowContainerController.positionChildAt(task.getWindowContainerController(), position);
+ mWindowContainerController.positionChildAt(task.getTask(), position);
updateTaskMovement(task, true);
}
@@ -4649,9 +4648,6 @@
r.mUserId, System.identityHashCode(r),
r.getTaskRecord().taskId, r.shortComponentName,
"proc died without state saved");
- if (r.getState() == RESUMED) {
- mService.updateUsageStats(r, false);
- }
}
} else {
// We have the current state for this activity, so
@@ -4912,7 +4908,7 @@
// TODO: Can only be called from special methods in ActivityStackSupervisor.
// Need to consolidate those calls points into this resize method so anyone can call directly.
void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds) {
- if (!updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) {
+ if (!updateBoundsAllowed(bounds)) {
return;
}
@@ -4926,20 +4922,7 @@
for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
final TaskRecord task = mTaskHistory.get(i);
if (task.isResizeable()) {
- if (inFreeformWindowingMode()) {
- // TODO(b/71028874): Can be removed since each freeform task is its own
- // stack.
- // For freeform stack we don't adjust the size of the tasks to match that
- // of the stack, but we do try to make sure the tasks are still contained
- // with the bounds of the stack.
- if (task.getRequestedOverrideBounds() != null) {
- mTmpRect2.set(task.getRequestedOverrideBounds());
- fitWithinBounds(mTmpRect2, bounds);
- task.updateOverrideConfiguration(mTmpRect2);
- }
- } else {
- task.updateOverrideConfiguration(taskBounds, insetBounds);
- }
+ task.updateOverrideConfiguration(taskBounds, insetBounds);
}
if (task.hasDisplayedBounds()) {
@@ -4951,7 +4934,6 @@
}
}
- mWindowContainerController.resize(bounds, mTmpBounds, mTmpInsetBounds);
setBounds(bounds);
}
@@ -4961,41 +4943,37 @@
/**
- * Adjust bounds to stay within stack bounds.
- *
- * Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
- * that keep them unchanged, but be contained within the stack bounds.
- *
- * @param bounds Bounds to be adjusted.
- * @param stackBounds Bounds within which the other bounds should remain.
+ * Until we can break this "set task bounds to same as stack bounds" behavior, this
+ * basically resizes both stack and task bounds to the same bounds.
*/
- private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
- if (stackBounds == null || stackBounds.isEmpty() || stackBounds.contains(bounds)) {
+ void setTaskBounds(Rect bounds) {
+ if (!updateBoundsAllowed(bounds)) {
return;
}
- if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
- final int maxRight = stackBounds.right
- - (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
- int horizontalDiff = stackBounds.left - bounds.left;
- if ((horizontalDiff < 0 && bounds.left >= maxRight)
- || (bounds.left + horizontalDiff >= maxRight)) {
- horizontalDiff = maxRight - bounds.left;
+ for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
+ final TaskRecord task = mTaskHistory.get(i);
+ if (task.isResizeable()) {
+ task.setBounds(bounds);
+ } else {
+ task.setBounds(null);
}
- bounds.left += horizontalDiff;
- bounds.right += horizontalDiff;
+ }
+ }
+
+ /** Helper to setDisplayedBounds on all child tasks */
+ void setTaskDisplayedBounds(Rect bounds) {
+ if (!updateDisplayedBoundsAllowed(bounds)) {
+ return;
}
- if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
- final int maxBottom = stackBounds.bottom
- - (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
- int verticalDiff = stackBounds.top - bounds.top;
- if ((verticalDiff < 0 && bounds.top >= maxBottom)
- || (bounds.top + verticalDiff >= maxBottom)) {
- verticalDiff = maxBottom - bounds.top;
+ for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
+ final TaskRecord task = mTaskHistory.get(i);
+ if (bounds == null || bounds.isEmpty()) {
+ task.setDisplayedBounds(null);
+ } else if (task.isResizeable()) {
+ task.setDisplayedBounds(bounds);
}
- bounds.top += verticalDiff;
- bounds.bottom += verticalDiff;
}
}
@@ -5349,7 +5327,7 @@
&& !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
task.updateOverrideConfiguration(getRequestedOverrideBounds());
}
- task.createWindowContainer(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
+ task.createTask(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
return task;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index e761ad8..4339e51 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1868,7 +1868,7 @@
stack.addTask(task, onTop, "restoreRecentTask");
// TODO: move call for creation here and other place into Stack.addTask()
- task.createWindowContainer(onTop, true /* showForAllUsers */);
+ task.createTask(onTop, true /* showForAllUsers */);
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
"Added restored task=" + task + " to stack=" + stack);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -2040,9 +2040,6 @@
mStoppingActivities.remove(r);
final ActivityStack stack = r.getActivityStack();
- if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
- mService.updateUsageStats(r, true);
- }
if (stack.getDisplay().allResumedActivitiesComplete()) {
mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
// Make sure activity & window visibility should be identical
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 182d1a0..9861157 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3799,7 +3799,7 @@
// changed, so we should reflect that check here as well.
final PinnedActivityStack stack = r.getActivityStack();
final PinnedStackWindowController windowController = stack.getWindowContainerController();
- return !windowController.isAnimatingBoundsToFullscreen();
+ return !windowController.mContainer.isAnimatingBoundsToFullscreen();
}
@Override
@@ -5234,13 +5234,20 @@
mH.post(mAmInternal::updateCpuStats);
}
- void updateUsageStats(ActivityRecord component, boolean resumed) {
- final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats,
+ void updateBatteryStats(ActivityRecord component, boolean resumed) {
+ final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateBatteryStats,
mAmInternal, component.mActivityComponent, component.app.mUid, component.mUserId,
resumed);
mH.sendMessage(m);
}
+ void updateActivityUsageStats(ActivityRecord activity, int event) {
+ final Message m = PooledLambda.obtainMessage(
+ ActivityManagerInternal::updateActivityUsageStats, mAmInternal,
+ activity.mActivityComponent, activity.mUserId, event, activity.appToken);
+ mH.sendMessage(m);
+ }
+
void setBooting(boolean booting) {
mAmInternal.setBooting(booting);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index c458c94..8624bff 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -746,6 +746,9 @@
@Override
void removeImmediately() {
onRemovedFromDisplay();
+ if (mActivityRecord != null) {
+ mActivityRecord.unregisterConfigurationChangeListener(this);
+ }
super.removeImmediately();
}
@@ -1175,21 +1178,14 @@
}
}
- void reparent(TaskWindowContainerController taskController, int position) {
+ void reparent(Task task, int position) {
if (DEBUG_ADD_REMOVE) {
Slog.i(TAG_WM, "reparent: moving app token=" + this
- + " to task=" + taskController + " at " + position);
+ + " to task=" + task.mTaskId + " at " + position);
}
- final Task task = taskController.mContainer;
if (task == null) {
- throw new IllegalArgumentException("reparent: could not find task="
- + taskController);
+ throw new IllegalArgumentException("reparent: could not find task");
}
- reparent(task, position);
- getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
-
- void reparent(Task task, int position) {
final Task currentTask = getTask();
if (task == currentTask) {
throw new IllegalArgumentException(
@@ -1220,6 +1216,7 @@
onDisplayChanged(displayContent);
prevDisplayContent.setLayoutNeeded();
}
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
}
@Override
diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java
index 1c7ebd6..2a05af4 100644
--- a/services/core/java/com/android/server/wm/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/wm/PinnedActivityStack.java
@@ -77,7 +77,7 @@
}
boolean isAnimatingBoundsToFullscreen() {
- return getWindowContainerController().isAnimatingBoundsToFullscreen();
+ return getWindowContainerController().mContainer.isAnimatingBoundsToFullscreen();
}
/**
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index bbdcc62..518e39b 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
@@ -180,15 +181,6 @@
}
/**
- * @return whether the bounds are currently animating to fullscreen.
- */
- public boolean isAnimatingBoundsToFullscreen() {
- synchronized (mGlobalLock) {
- return mContainer.isAnimatingBoundsToFullscreen();
- }
- }
-
- /**
* @return whether the stack can be resized from the bounds animation.
*/
public boolean pinnedStackResizeDisallowed() {
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 8f18aa5..ada807b 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -21,7 +21,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
@@ -29,8 +28,6 @@
import android.os.Message;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
@@ -49,11 +46,7 @@
private final H mHandler;
- // Temp bounds only used in adjustConfigurationForBounds()
- private final Rect mTmpRect = new Rect();
- private final Rect mTmpStableInsets = new Rect();
- private final Rect mTmpNonDecorInsets = new Rect();
- private final Rect mTmpDisplayBounds = new Rect();
+ final Rect mTmpBounds = new Rect();
public StackWindowController(int stackId, StackWindowListener listener, int displayId,
boolean onTop, Rect outBounds) {
@@ -67,107 +60,87 @@
mStackId = stackId;
mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
- synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
- throw new IllegalArgumentException("Trying to add stackId=" + stackId
- + " to unknown displayId=" + displayId);
- }
-
- dc.createStack(stackId, onTop, this);
- getRawBounds(outBounds);
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc == null) {
+ throw new IllegalArgumentException("Trying to add stackId=" + stackId
+ + " to unknown displayId=" + displayId);
}
+
+ dc.createStack(stackId, onTop, this);
+ getRawBounds(outBounds);
}
@Override
public void removeContainer() {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.removeIfPossible();
- super.removeContainer();
- }
+ if (mContainer != null) {
+ mContainer.removeIfPossible();
+ super.removeContainer();
}
}
- public void reparent(int displayId, Rect outStackBounds, boolean onTop) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
- + " to displayId=" + displayId);
- }
-
- final DisplayContent targetDc = mRoot.getDisplayContent(displayId);
- if (targetDc == null) {
- throw new IllegalArgumentException("Trying to move stackId=" + mStackId
- + " to unknown displayId=" + displayId);
- }
-
- targetDc.moveStackToDisplay(mContainer, onTop);
- getRawBounds(outStackBounds);
+ void reparent(int displayId, Rect outStackBounds, boolean onTop) {
+ if (mContainer == null) {
+ throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
+ + " to displayId=" + displayId);
}
+
+ final DisplayContent targetDc = mRoot.getDisplayContent(displayId);
+ if (targetDc == null) {
+ throw new IllegalArgumentException("Trying to move stackId=" + mStackId
+ + " to unknown displayId=" + displayId);
+ }
+
+ targetDc.moveStackToDisplay(mContainer, onTop);
+ getRawBounds(outStackBounds);
}
- public void positionChildAt(TaskWindowContainerController child, int position) {
- synchronized (mGlobalLock) {
- if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning task=" + child
- + " at " + position);
- if (child.mContainer == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM,
- "positionChildAt: could not find task=" + this);
- return;
- }
- if (mContainer == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM,
- "positionChildAt: could not find stack for task=" + mContainer);
- return;
- }
- child.mContainer.positionAt(position);
- mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ void positionChildAt(Task child, int position) {
+ if (DEBUG_STACK) {
+ Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position);
}
+ if (child == null) {
+ if (DEBUG_STACK) {
+ Slog.i(TAG_WM, "positionChildAt: could not find task=" + this);
+ }
+ return;
+ }
+ if (mContainer == null) {
+ if (DEBUG_STACK) {
+ Slog.i(TAG_WM, "positionChildAt: could not find stack for task=" + mContainer);
+ }
+ return;
+ }
+ child.positionAt(position);
+ mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
}
- public void positionChildAtTop(TaskWindowContainerController child, boolean includingParents) {
+ void positionChildAtTop(Task child, boolean includingParents) {
if (child == null) {
// TODO: Fix the call-points that cause this to happen.
return;
}
- synchronized (mGlobalLock) {
- final Task childTask = child.mContainer;
- if (childTask == null) {
- Slog.e(TAG_WM, "positionChildAtTop: task=" + child + " not found");
- return;
- }
- mContainer.positionChildAt(POSITION_TOP, childTask, includingParents);
+ mContainer.positionChildAt(POSITION_TOP, child, includingParents);
- final DisplayContent displayContent = mContainer.getDisplayContent();
- if (displayContent.mAppTransition.isTransitionSet()) {
- childTask.setSendingToBottom(false);
- }
- displayContent.layoutAndAssignWindowLayersIfNeeded();
+ final DisplayContent displayContent = mContainer.getDisplayContent();
+ if (displayContent.mAppTransition.isTransitionSet()) {
+ child.setSendingToBottom(false);
}
+ displayContent.layoutAndAssignWindowLayersIfNeeded();
}
- public void positionChildAtBottom(TaskWindowContainerController child,
- boolean includingParents) {
+ void positionChildAtBottom(Task child, boolean includingParents) {
if (child == null) {
// TODO: Fix the call-points that cause this to happen.
return;
}
- synchronized (mGlobalLock) {
- final Task childTask = child.mContainer;
- if (childTask == null) {
- Slog.e(TAG_WM, "positionChildAtBottom: task=" + child + " not found");
- return;
- }
- mContainer.positionChildAt(POSITION_BOTTOM, childTask, includingParents);
+ mContainer.positionChildAt(POSITION_BOTTOM, child, includingParents);
- if (mContainer.getDisplayContent().mAppTransition.isTransitionSet()) {
- childTask.setSendingToBottom(true);
- }
- mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ if (mContainer.getDisplayContent().mAppTransition.isTransitionSet()) {
+ child.setSendingToBottom(true);
}
+ mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
}
/**
@@ -179,24 +152,20 @@
*/
public void resize(Rect bounds, SparseArray<Rect> taskBounds,
SparseArray<Rect> taskTempInsetBounds) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
- }
- // We might trigger a configuration change. Save the current task bounds for freezing.
- mContainer.prepareFreezingTaskBounds();
- if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds)
- && mContainer.isVisible()) {
- mContainer.getDisplayContent().setLayoutNeeded();
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
+ if (mContainer == null) {
+ throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
+ }
+ // We might trigger a configuration change. Save the current task bounds for freezing.
+ mContainer.prepareFreezingTaskBounds();
+ if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds)
+ && mContainer.isVisible()) {
+ mContainer.getDisplayContent().setLayoutNeeded();
+ mService.mWindowPlacerLocked.performSurfacePlacement();
}
}
public void onPipAnimationEndResize() {
- synchronized (mService.mGlobalLock) {
- mContainer.onPipAnimationEndResize();
- }
+ mContainer.onPipAnimationEndResize();
}
/**
@@ -205,167 +174,37 @@
public void getStackDockedModeBounds(Configuration parentConfig, Rect dockedBounds,
Rect currentTempTaskBounds,
Rect outStackBounds, Rect outTempTaskBounds) {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getStackDockedModeBoundsLocked(parentConfig, dockedBounds,
- currentTempTaskBounds, outStackBounds, outTempTaskBounds);
- return;
- }
- outStackBounds.setEmpty();
- outTempTaskBounds.setEmpty();
+ if (mContainer != null) {
+ mContainer.getStackDockedModeBoundsLocked(parentConfig, dockedBounds,
+ currentTempTaskBounds, outStackBounds, outTempTaskBounds);
+ return;
}
+ outStackBounds.setEmpty();
+ outTempTaskBounds.setEmpty();
}
public void prepareFreezingTaskBounds() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
- + " not found.");
- }
- mContainer.prepareFreezingTaskBounds();
+ if (mContainer == null) {
+ throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
+ + " not found.");
}
+ mContainer.prepareFreezingTaskBounds();
}
public void getRawBounds(Rect outBounds) {
- synchronized (mGlobalLock) {
- if (mContainer.matchParentBounds()) {
- outBounds.setEmpty();
- } else {
- mContainer.getRawBounds(outBounds);
- }
+ if (mContainer.matchParentBounds()) {
+ outBounds.setEmpty();
+ } else {
+ mContainer.getRawBounds(outBounds);
}
}
public void getBounds(Rect outBounds) {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getBounds(outBounds);
- return;
- }
- outBounds.setEmpty();
+ if (mContainer != null) {
+ mContainer.getBounds(outBounds);
+ return;
}
- }
-
- /**
- * Adjusts the screen size in dp's for the {@param config} for the given params. The provided
- * params represent the desired state of a configuration change. Since this utility is used
- * before mContainer has been updated, any relevant properties (like {@param windowingMode})
- * need to be passed in.
- */
- public void adjustConfigurationForBounds(Rect bounds,
- Rect nonDecorBounds, Rect stableBounds, boolean overrideWidth,
- boolean overrideHeight, float density, Configuration config,
- Configuration parentConfig, int windowingMode) {
- synchronized (mGlobalLock) {
- final TaskStack stack = mContainer;
- 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
- displayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- displayCutout, mTmpStableInsets);
- displayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- displayCutout, mTmpNonDecorInsets);
- mTmpDisplayBounds.set(0, 0, di.logicalWidth, di.logicalHeight);
-
- int width;
- int height;
-
- final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
-
- config.windowConfiguration.setBounds(bounds);
- config.windowConfiguration.setAppBounds(!bounds.isEmpty() ? bounds : null);
- boolean intersectParentBounds = false;
-
- if (WindowConfiguration.isFloating(windowingMode)) {
- // Floating tasks should not be resized to the screen's bounds.
-
- if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED
- && bounds.width() == mTmpDisplayBounds.width()
- && bounds.height() == mTmpDisplayBounds.height()) {
- // If the bounds we are animating is the same as the fullscreen stack
- // dimensions, then apply the same inset calculations that we normally do for
- // the fullscreen stack, without intersecting it with the display bounds
- stableBounds.inset(mTmpStableInsets);
- nonDecorBounds.inset(mTmpNonDecorInsets);
- // Move app bounds to zero to apply intersection with parent correctly. They are
- // used only for evaluating width and height, so it's OK to move them around.
- config.windowConfiguration.getAppBounds().offsetTo(0, 0);
- intersectParentBounds = true;
- }
- width = (int) (stableBounds.width() / density);
- height = (int) (stableBounds.height() / density);
- } else {
- // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
- // area, i.e. the screen area without the system bars.
- // Additionally task dimensions should not be bigger than its parents dimensions.
- // The non decor inset are areas that could never be removed in Honeycomb. See
- // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
- intersectDisplayBoundsExcludeInsets(nonDecorBounds, bounds, mTmpNonDecorInsets,
- mTmpDisplayBounds, overrideWidth, overrideHeight);
- intersectDisplayBoundsExcludeInsets(stableBounds, bounds, mTmpStableInsets,
- mTmpDisplayBounds, overrideWidth, overrideHeight);
- width = Math.min((int) (stableBounds.width() / density),
- parentConfig.screenWidthDp);
- height = Math.min((int) (stableBounds.height() / density),
- parentConfig.screenHeightDp);
- intersectParentBounds = true;
- }
-
- if (intersectParentBounds && config.windowConfiguration.getAppBounds() != null) {
- config.windowConfiguration.getAppBounds().intersect(parentAppBounds);
- }
-
- config.screenWidthDp = width;
- config.screenHeightDp = height;
- config.smallestScreenWidthDp = getSmallestWidthForTaskBounds(
- bounds, density, windowingMode);
- }
- }
-
- /**
- * Intersects the specified {@code inOutBounds} with the display frame that excludes the stable
- * inset areas.
- *
- * @param inOutBounds The inOutBounds to subtract the stable inset areas from.
- */
- private void intersectDisplayBoundsExcludeInsets(Rect inOutBounds, Rect inInsetBounds,
- Rect stableInsets, Rect displayBounds, boolean overrideWidth, boolean overrideHeight) {
- mTmpRect.set(inInsetBounds);
- mService.intersectDisplayInsetBounds(displayBounds, stableInsets, mTmpRect);
- int leftInset = mTmpRect.left - inInsetBounds.left;
- int topInset = mTmpRect.top - inInsetBounds.top;
- int rightInset = overrideWidth ? 0 : inInsetBounds.right - mTmpRect.right;
- int bottomInset = overrideHeight ? 0 : inInsetBounds.bottom - mTmpRect.bottom;
- inOutBounds.inset(leftInset, topInset, rightInset, bottomInset);
- }
-
- /**
- * Calculates the smallest width for a task given the target {@param bounds} and
- * {@param windowingMode}. Avoid using values from mContainer since they can be out-of-date.
- *
- * @return the smallest width to be used in the Configuration, in dips
- */
- private int getSmallestWidthForTaskBounds(Rect bounds, float density, int windowingMode) {
- final DisplayContent displayContent = mContainer.getDisplayContent();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-
- if (bounds == null || (bounds.width() == displayInfo.logicalWidth &&
- bounds.height() == displayInfo.logicalHeight)) {
- // If the bounds are fullscreen, return the value of the fullscreen configuration
- return displayContent.getConfiguration().smallestScreenWidthDp;
- } else if (WindowConfiguration.isFloating(windowingMode)) {
- // For floating tasks, calculate the smallest width from the bounds of the task
- return (int) (Math.min(bounds.width(), bounds.height()) / density);
- } else {
- // Iterating across all screen orientations, and return the minimum of the task
- // width taking into account that the bounds might change because the snap algorithm
- // snaps to a different value
- return displayContent.getDockedDividerController()
- .getSmallestWidthDpForBounds(bounds);
- }
+ outBounds.setEmpty();
}
void requestResize(Rect bounds) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 67657d0..b10fd31 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -24,6 +24,7 @@
import static android.content.res.Configuration.EMPTY;
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
@@ -38,6 +39,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.CallSuper;
+import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -54,7 +56,7 @@
import java.io.PrintWriter;
import java.util.function.Consumer;
-class Task extends WindowContainer<AppWindowToken> {
+class Task extends WindowContainer<AppWindowToken> implements ConfigurationContainerListener{
static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
// TODO: Track parent marks like this in WindowContainer.
@@ -109,16 +111,24 @@
/** @see #setCanAffectSystemUiFlags */
private boolean mCanAffectSystemUiFlags = true;
+ // TODO: remove after unification
+ TaskRecord mTaskRecord;
+
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
boolean supportsPictureInPicture, TaskDescription taskDescription,
- TaskWindowContainerController controller) {
+ TaskRecord taskRecord) {
super(service);
mTaskId = taskId;
mStack = stack;
mUserId = userId;
mResizeMode = resizeMode;
mSupportsPictureInPicture = supportsPictureInPicture;
- setController(controller);
+ mTaskRecord = taskRecord;
+ if (mTaskRecord != null) {
+ // This can be null when we call createTaskInStack in WindowTestUtils. Remove this after
+ // unification.
+ mTaskRecord.registerConfigurationChangeListener(this);
+ }
setBounds(getRequestedOverrideBounds());
mTaskDescription = taskDescription;
@@ -191,10 +201,28 @@
if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeTask");
mDeferRemoval = false;
+ if (mTaskRecord != null) {
+ mTaskRecord.unregisterConfigurationChangeListener(this);
+ }
super.removeImmediately();
}
+ void reparent(StackWindowController stackController, int position, boolean moveParents) {
+ if (DEBUG_STACK) {
+ Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId
+ + " to stack=" + stackController + " at " + position);
+ }
+ final TaskStack stack = stackController.mContainer;
+ if (stack == null) {
+ throw new IllegalArgumentException("reparent: could not find stack="
+ + stackController);
+ }
+ reparent(stack, position, moveParents);
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ }
+
+
void reparent(TaskStack stack, int position, boolean moveParents) {
if (stack == mStack) {
throw new IllegalArgumentException(
@@ -300,6 +328,12 @@
return boundsChange;
}
+ void resize(boolean relayout, boolean forced) {
+ if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) {
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ }
+ }
+
@Override
void onDisplayChanged(DisplayContent dc) {
adjustBoundsForDisplayChangeIfNeeded(dc);
@@ -515,6 +549,15 @@
return mDragResizeMode;
}
+ /**
+ * Puts this task into docked drag resizing mode. See {@link DragResizeMode}.
+ *
+ * @param resizing Whether to put the task into drag resize mode.
+ */
+ public void setTaskDockedResizing(boolean resizing) {
+ setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+ }
+
private void adjustBoundsForDisplayChangeIfNeeded(final DisplayContent displayContent) {
if (displayContent == null) {
return;
@@ -556,9 +599,8 @@
displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
if (setBounds(mTmpRect2) != BOUNDS_CHANGE_NONE) {
- final TaskWindowContainerController controller = getController();
- if (controller != null) {
- controller.requestResize(getBounds(), RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
+ if (mTaskRecord != null) {
+ mTaskRecord.requestResize(getBounds(), RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
}
}
}
@@ -631,6 +673,20 @@
return null;
}
+ void positionChildAtTop(AppWindowToken aToken) {
+ positionChildAt(aToken, POSITION_TOP);
+ }
+
+ void positionChildAt(AppWindowToken aToken, int position) {
+ if (aToken == null) {
+ Slog.w(TAG_WM,
+ "Attempted to position of non-existing app");
+ return;
+ }
+
+ positionChildAt(position, aToken, false /* includeParents */);
+ }
+
boolean isFullscreen() {
if (useCurrentBounds()) {
return matchParentBounds();
@@ -656,6 +712,10 @@
mTaskDescription = taskDescription;
}
+ void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
+ mTaskRecord.onSnapshotChanged(snapshot);
+ }
+
TaskDescription getTaskDescription() {
return mTaskDescription;
}
@@ -666,11 +726,6 @@
}
@Override
- TaskWindowContainerController getController() {
- return (TaskWindowContainerController) super.getController();
- }
-
- @Override
void forAllTasks(Consumer<Task> callback) {
callback.accept(this);
}
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index b6a6009..5bb6440 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -27,6 +27,7 @@
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.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
@@ -47,6 +48,7 @@
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.server.EventLogTags.WM_TASK_CREATED;
import static com.android.server.am.TaskRecordProto.ACTIVITIES;
import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
import static com.android.server.am.TaskRecordProto.BOUNDS;
@@ -76,10 +78,15 @@
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.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 static java.lang.Integer.MAX_VALUE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
@@ -106,8 +113,10 @@
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.DisplayMetrics;
+import android.util.EventLog;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
@@ -125,8 +134,7 @@
import java.util.ArrayList;
import java.util.Objects;
-// TODO: Make package private again once move to WM package is complete.
-public class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
+class TaskRecord extends ConfigurationContainer {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_ATM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -190,6 +198,13 @@
// Do not move the stack as a part of reparenting
static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
+ // The height/width divide used when fitting a task within a bounds with method
+ // {@link #fitWithinBounds}.
+ // We always want the task to to be visible in the bounds without affecting its size when
+ // fitting. To make sure this is the case, we don't adjust the task left or top side pass
+ // the input bounds right or bottom side minus the width or height divided by this value.
+ private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
+
/**
* The factory used to create {@link TaskRecord}. This allows OEM subclass {@link TaskRecord}.
*/
@@ -295,7 +310,8 @@
private final Rect mTmpStableBounds = new Rect();
private final Rect mTmpNonDecorBounds = new Rect();
- private final Rect mTmpRect = new Rect();
+ private final Rect mTmpBounds = new Rect();
+ private final Rect mTmpInsets = new Rect();
// Last non-fullscreen bounds the task was launched in or resized to.
// The information is persisted and used to determine the appropriate stack to launch the
@@ -318,7 +334,8 @@
/** Helper object used for updating override configuration. */
private Configuration mTmpConfig = new Configuration();
- private TaskWindowContainerController mWindowContainerController;
+ // TODO: remove after unification
+ Task mTask;
/**
* Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
@@ -424,43 +441,54 @@
mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
}
- TaskWindowContainerController getWindowContainerController() {
- return mWindowContainerController;
+ Task getTask() {
+ return mTask;
}
- void createWindowContainer(boolean onTop, boolean showForAllUsers) {
- if (mWindowContainerController != null) {
- throw new IllegalArgumentException("Window container=" + mWindowContainerController
+ void createTask(boolean onTop, boolean showForAllUsers) {
+ if (mTask != null) {
+ throw new IllegalArgumentException("mTask=" + mTask
+ " already created for task=" + this);
}
final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
- setWindowContainerController(new TaskWindowContainerController(taskId, this,
- getStack().getWindowContainerController(), userId, bounds,
- mResizeMode, mSupportsPictureInPicture, onTop,
- showForAllUsers, lastTaskDescription));
+ final StackWindowController stackController = getStack().getWindowContainerController();
+
+ if (DEBUG_STACK) {
+ Slog.i(TAG_WM, "TaskRecord: taskId=" + taskId
+ + " stack=" + stackController + " bounds=" + bounds);
+ }
+
+ final TaskStack stack = stackController.mContainer;
+ if (stack == null) {
+ throw new IllegalArgumentException("TaskRecord: invalid stack="
+ + stackController);
+ }
+ EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
+ mTask = new Task(taskId, stack, userId, mService.mWindowManager, mResizeMode,
+ mSupportsPictureInPicture, lastTaskDescription, this);
+ final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
+
+ if (!mDisplayedBounds.isEmpty()) {
+ mTask.setOverrideDisplayedBounds(mDisplayedBounds);
+ }
+ // We only want to move the parents to the parents if we are creating this task at the
+ // top of its stack.
+ stack.addTask(mTask, position, showForAllUsers, onTop /* moveParents */);
}
- /**
- * Should only be invoked from {@link #createWindowContainer(boolean, boolean)}.
- */
- @VisibleForTesting
- protected void setWindowContainerController(TaskWindowContainerController controller) {
- if (mWindowContainerController != null) {
- throw new IllegalArgumentException("Window container=" + mWindowContainerController
- + " already created for task=" + this);
- }
-
- mWindowContainerController = controller;
- if (!mDisplayedBounds.isEmpty() && controller.mContainer != null) {
- controller.mContainer.setOverrideDisplayedBounds(mDisplayedBounds);
- }
+ void setTask(Task task) {
+ mTask = task;
}
void removeWindowContainer() {
mService.getLockTaskController().clearLockedTask(this);
- mWindowContainerController.removeContainer();
- mWindowContainerController = null;
+ if (mTask == null) {
+ if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: could not find taskId=" + taskId);
+ return;
+ }
+ mTask.removeIfPossible();
+ mTask = null;
if (!getWindowConfiguration().persistTaskBounds()) {
// Reset current bounds for task whose bounds shouldn't be persisted so it uses
// default configuration the next time it launches.
@@ -469,7 +497,6 @@
mService.getTaskChangeNotificationController().notifyTaskRemoved(taskId);
}
- @Override
public void onSnapshotChanged(TaskSnapshot snapshot) {
mService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(taskId, snapshot);
}
@@ -479,17 +506,20 @@
return;
}
mResizeMode = resizeMode;
- mWindowContainerController.setResizeable(resizeMode);
+ mTask.setResizeable(resizeMode);
mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
void setTaskDockedResizing(boolean resizing) {
- mWindowContainerController.setTaskDockedResizing(resizing);
+ if (mTask == null) {
+ Slog.w(TAG_WM, "setTaskDockedResizing: taskId " + taskId + " not found.");
+ return;
+ }
+ mTask.setTaskDockedResizing(resizing);
}
// TODO: Consolidate this with the resize() method below.
- @Override
public void requestResize(Rect bounds, int resizeMode) {
mService.resizeTask(taskId, bounds, resizeMode);
}
@@ -511,7 +541,7 @@
return true;
}
- if (mWindowContainerController == null) {
+ if (mTask == null) {
// Task doesn't exist in window manager yet (e.g. was restored from recents).
// All we can do for now is update the bounds so it can be used when the task is
// added to window manager.
@@ -558,7 +588,7 @@
}
}
}
- mWindowContainerController.resize(kept, forced);
+ mTask.resize(kept, forced);
saveLaunchingStateIfNeeded();
@@ -571,11 +601,15 @@
// TODO: Investigate combining with the resize() method above.
void resizeWindowContainer() {
- mWindowContainerController.resize(false /* relayout */, false /* forced */);
+ mTask.resize(false /* relayout */, false /* forced */);
}
void getWindowContainerBounds(Rect bounds) {
- mWindowContainerController.getBounds(bounds);
+ if (mTask != null) {
+ mTask.getBounds(bounds);
+ } else {
+ bounds.setEmpty();
+ }
}
/**
@@ -679,7 +713,7 @@
// Must reparent first in window manager to avoid a situation where AM can delete the
// we are coming from in WM before we reparent because it became empty.
- mWindowContainerController.reparent(toStack.getWindowContainerController(), position,
+ mTask.reparent(toStack.getWindowContainerController(), position,
moveStackMode == REPARENT_MOVE_STACK_TO_FRONT);
final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
@@ -779,7 +813,11 @@
}
void cancelWindowTransition() {
- mWindowContainerController.cancelWindowTransition();
+ if (mTask == null) {
+ Slog.w(TAG_WM, "cancelWindowTransition: taskId " + taskId + " not found.");
+ return;
+ }
+ mTask.cancelTaskWindowTransition();
}
/**
@@ -1190,7 +1228,7 @@
mActivities.add(newTop);
// Make sure window manager is aware of the position change.
- mWindowContainerController.positionChildAtTop(newTop.mAppWindowToken);
+ mTask.positionChildAtTop(newTop.mAppWindowToken);
updateEffectiveIntent();
setFrontOfTask();
@@ -1275,7 +1313,7 @@
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(r.mAppWindowToken, index);
+ mTask.positionChildAt(r.mAppWindowToken, index);
}
// Make sure the list of display UID whitelists is updated
@@ -1643,8 +1681,8 @@
}
lastTaskDescription = new TaskDescription(label, null, iconResource, iconFilename,
colorPrimary, colorBackground, statusBarColor, navigationBarColor);
- if (mWindowContainerController != null) {
- mWindowContainerController.setTaskDescription(lastTaskDescription);
+ if (mTask != null) {
+ mTask.setTaskDescription(lastTaskDescription);
}
// Update the task affiliation color if we are the parent of the group
if (taskId == mAffiliatedTaskId) {
@@ -1687,7 +1725,7 @@
// If the task has no requested minimal size, we'd like to enforce a minimal size
// so that the user can not render the task too small to manipulate. We don't need
// to do this for the pinned stack as the bounds are controlled by the system.
- if (!inPinnedWindowingMode()) {
+ if (!inPinnedWindowingMode() && mStack != null) {
final int defaultMinSizeDp =
mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp;
final ActivityDisplay display =
@@ -1731,31 +1769,6 @@
}
/**
- * @return a new Configuration for this Task, given the provided {@param bounds} and
- * {@param insetBounds}.
- */
- Configuration computeNewOverrideConfigurationForBounds(Rect bounds, Rect insetBounds) {
- // Compute a new override configuration for the given bounds, if fullscreen bounds
- // (bounds == null), then leave the override config unset
- final Configuration newOverrideConfig = new Configuration();
- if (bounds != null) {
- newOverrideConfig.setTo(getRequestedOverrideConfiguration());
- if (insetBounds != null && !insetBounds.isEmpty()) {
- mTmpRect.set(insetBounds);
- setDisplayedBounds(bounds);
- } else {
- mTmpRect.set(bounds);
- setDisplayedBounds(null);
- }
- adjustForMinimalTaskDimensions(mTmpRect);
- computeOverrideConfiguration(newOverrideConfig, mTmpRect,
- mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
- }
-
- return newOverrideConfig;
- }
-
- /**
* Update task's override configuration based on the bounds.
* @param bounds The bounds of the task.
* @return True if the override configuration was updated.
@@ -1781,42 +1794,21 @@
* @return True if the override configuration was updated.
*/
boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
- if (equivalentRequestedOverrideBounds(bounds)) {
+ final boolean hasSetDisplayedBounds = (insetBounds != null && !insetBounds.isEmpty());
+ if (hasSetDisplayedBounds) {
+ setDisplayedBounds(bounds);
+ } else {
+ setDisplayedBounds(null);
+ }
+ // "steady" bounds do not include any temporary offsets from animation or interaction.
+ Rect steadyBounds = hasSetDisplayedBounds ? insetBounds : bounds;
+ if (equivalentRequestedOverrideBounds(steadyBounds)) {
return false;
}
- final Rect currentBounds = getRequestedOverrideBounds();
- mTmpConfig.setTo(getRequestedOverrideConfiguration());
- final Configuration newConfig = getRequestedOverrideConfiguration();
-
- final boolean matchParentBounds = bounds == null || bounds.isEmpty();
- final boolean persistBounds = getWindowConfiguration().persistTaskBounds();
- if (matchParentBounds) {
- if (!currentBounds.isEmpty() && persistBounds) {
- setLastNonFullscreenBounds(currentBounds);
- }
- setBounds(null);
- setDisplayedBounds(null);
- newConfig.unset();
- } else {
- if (insetBounds != null && !insetBounds.isEmpty()) {
- mTmpRect.set(insetBounds);
- setDisplayedBounds(bounds);
- } else {
- mTmpRect.set(bounds);
- setDisplayedBounds(null);
- }
- adjustForMinimalTaskDimensions(mTmpRect);
- setBounds(mTmpRect);
-
- if (mStack == null || persistBounds) {
- setLastNonFullscreenBounds(getRequestedOverrideBounds());
- }
- computeOverrideConfiguration(newConfig, mTmpRect,
- mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
- }
- onRequestedOverrideConfigurationChanged(newConfig);
- return !mTmpConfig.equals(newConfig);
+ mTmpConfig.setTo(getResolvedOverrideConfiguration());
+ setBounds(steadyBounds);
+ return !mTmpConfig.equals(getResolvedOverrideConfiguration());
}
/**
@@ -1842,6 +1834,12 @@
if (wasInMultiWindowMode != inMultiWindowMode()) {
mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
}
+ if (getWindowConfiguration().persistTaskBounds()) {
+ final Rect currentBounds = getRequestedOverrideBounds();
+ if (!currentBounds.isEmpty()) {
+ setLastNonFullscreenBounds(currentBounds);
+ }
+ }
// TODO: Should also take care of Pip mode changes here.
saveLaunchingStateIfNeeded();
@@ -1869,6 +1867,45 @@
}
/**
+ * Adjust bounds to stay within stack bounds.
+ *
+ * Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
+ * that keep them unchanged, but be contained within the stack bounds.
+ *
+ * @param bounds Bounds to be adjusted.
+ * @param stackBounds Bounds within which the other bounds should remain.
+ */
+ private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
+ if (stackBounds == null || stackBounds.isEmpty() || stackBounds.contains(bounds)) {
+ return;
+ }
+
+ if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
+ final int maxRight = stackBounds.right
+ - (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
+ int horizontalDiff = stackBounds.left - bounds.left;
+ if ((horizontalDiff < 0 && bounds.left >= maxRight)
+ || (bounds.left + horizontalDiff >= maxRight)) {
+ horizontalDiff = maxRight - bounds.left;
+ }
+ bounds.left += horizontalDiff;
+ bounds.right += horizontalDiff;
+ }
+
+ if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
+ final int maxBottom = stackBounds.bottom
+ - (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
+ int verticalDiff = stackBounds.top - bounds.top;
+ if ((verticalDiff < 0 && bounds.top >= maxBottom)
+ || (bounds.top + verticalDiff >= maxBottom)) {
+ verticalDiff = maxBottom - bounds.top;
+ }
+ bounds.top += verticalDiff;
+ bounds.bottom += verticalDiff;
+ }
+ }
+
+ /**
* Displayed bounds are used to set where the task is drawn at any given time. This is
* separate from its actual bounds so that the app doesn't see any meaningful configuration
* changes during transitionary periods.
@@ -1879,9 +1916,8 @@
} else {
mDisplayedBounds.set(bounds);
}
- final TaskWindowContainerController controller = getWindowContainerController();
- if (controller != null && controller.mContainer != null) {
- controller.mContainer.setOverrideDisplayedBounds(
+ if (mTask != null) {
+ mTask.setOverrideDisplayedBounds(
mDisplayedBounds.isEmpty() ? null : mDisplayedBounds);
}
}
@@ -1901,46 +1937,205 @@
return !mDisplayedBounds.isEmpty();
}
- /** Clears passed config and fills it with new override values. */
- // TODO(b/36505427): TaskRecord.computeOverrideConfiguration() is a utility method that doesn't
- // depend on task or stacks, but uses those object to get the display to base the calculation
- // on. Probably best to centralize calculations like this in ConfigurationContainer.
- void computeOverrideConfiguration(Configuration config, Rect bounds,
- boolean overrideWidth, boolean overrideHeight) {
- mTmpNonDecorBounds.set(bounds);
- mTmpStableBounds.set(bounds);
+ /**
+ * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
+ * intersectBounds on a side, then the respective side will not be intersected.
+ *
+ * The assumption is that if inOutBounds is initially larger than intersectBounds, then the
+ * inset on that side is no-longer applicable. This scenario happens when a task's minimal
+ * bounds are larger than the provided parent/display bounds.
+ *
+ * @param inOutBounds the bounds to intersect.
+ * @param intersectBounds the bounds to intersect with.
+ * @param intersectInsets insets to apply to intersectBounds before intersecting.
+ */
+ private static void intersectWithInsetsIfFits(
+ Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
+ if (inOutBounds.right <= intersectBounds.right) {
+ inOutBounds.right =
+ Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
+ }
+ if (inOutBounds.bottom <= intersectBounds.bottom) {
+ inOutBounds.bottom =
+ Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
+ }
+ if (inOutBounds.left >= intersectBounds.left) {
+ inOutBounds.left =
+ Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
+ }
+ if (inOutBounds.top >= intersectBounds.top) {
+ inOutBounds.top =
+ Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
+ }
+ }
- config.unset();
- final Configuration parentConfig = getParent().getConfiguration();
+ /**
+ * Gets bounds with non-decor and stable insets applied respectively.
+ *
+ * If bounds overhangs the display, those edges will not get insets. See
+ * {@link #intersectWithInsetsIfFits}
+ *
+ * @param outNonDecorBounds where to place bounds with non-decor insets applied.
+ * @param outStableBounds where to place bounds with stable insets applied.
+ * @param bounds the bounds to inset.
+ */
+ private void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
+ DisplayInfo displayInfo) {
+ outNonDecorBounds.set(bounds);
+ outStableBounds.set(bounds);
+ if (getStack() == null || getStack().getDisplay() == null) {
+ return;
+ }
+ DisplayPolicy policy = getStack().getDisplay().mDisplayContent.getDisplayPolicy();
+ if (policy == null) {
+ return;
+ }
+ mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
- final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ policy.getStableInsetsLw(displayInfo.rotation,
+ displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout,
+ mTmpInsets);
+ intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
- if (mStack != null) {
- final StackWindowController stackController = mStack.getWindowContainerController();
- stackController.adjustConfigurationForBounds(bounds,
- mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
- config, parentConfig, getWindowingMode());
- } else {
- throw new IllegalArgumentException("Expected stack when calculating override config");
+ policy.getNonDecorInsetsLw(displayInfo.rotation,
+ displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout,
+ mTmpInsets);
+ intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
+ }
+
+ /**
+ * Asks docked-divider controller for the smallestwidthdp given bounds.
+ * @param bounds bounds to calculate smallestwidthdp for.
+ */
+ private int getSmallestScreenWidthDpForDockedBounds(Rect bounds) {
+ DisplayContent dc = mStack.getDisplay().mDisplayContent;
+ if (dc != null) {
+ return dc.getDockedDividerController().getSmallestWidthDpForBounds(bounds);
+ }
+ return Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+ }
+
+ /**
+ * Calculates configuration values used by the client to get resources. This should be run
+ * using app-facing bounds (bounds unmodified by animations or transient interactions).
+ *
+ * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
+ * configuring an "inherit-bounds" window which means that all configuration settings would
+ * just be inherited from the parent configuration.
+ **/
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Rect bounds,
+ @NonNull Configuration parentConfig) {
+ int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = parentConfig.windowConfiguration.getWindowingMode();
}
- config.orientation = (config.screenWidthDp <= config.screenHeightDp)
- ? Configuration.ORIENTATION_PORTRAIT
- : Configuration.ORIENTATION_LANDSCAPE;
+ float density = inOutConfig.densityDpi;
+ if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+ density = parentConfig.densityDpi;
+ }
+ density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
- // For calculating screen layout, we need to use the non-decor inset screen area for the
- // calculation for compatibility reasons, i.e. screen area without system bars that could
- // never go away in Honeycomb.
- final int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
- final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
- // We're only overriding LONG, SIZE and COMPAT parts of screenLayout, so we start override
- // calculation with partial default.
- // Reducing the screen layout starting from its parent config.
- final int sl = parentConfig.screenLayout &
- (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
- final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
- final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
- config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
+ Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (outAppBounds == null || outAppBounds.isEmpty()) {
+ inOutConfig.windowConfiguration.setAppBounds(bounds);
+ outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ }
+ if (windowingMode != WINDOWING_MODE_FREEFORM) {
+ final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
+ if (parentAppBounds != null && !parentAppBounds.isEmpty()) {
+ outAppBounds.intersect(parentAppBounds);
+ }
+ }
+
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
+ || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ if (mStack != null) {
+ final DisplayInfo di = new DisplayInfo();
+ mStack.getDisplay().mDisplay.getDisplayInfo(di);
+
+ // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
+ // area, i.e. the screen area without the system bars.
+ // The non decor inset are areas that could never be removed in Honeycomb. See
+ // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
+ calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, bounds, di);
+ } else {
+ mTmpNonDecorBounds.set(bounds);
+ mTmpStableBounds.set(bounds);
+ }
+
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ inOutConfig.screenWidthDp = Math.min((int) (mTmpStableBounds.width() / density),
+ parentConfig.screenWidthDp);
+ }
+ if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ inOutConfig.screenHeightDp = Math.min((int) (mTmpStableBounds.height() / density),
+ parentConfig.screenHeightDp);
+ }
+
+ if (inOutConfig.smallestScreenWidthDp
+ == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+ if (WindowConfiguration.isFloating(windowingMode)) {
+ // For floating tasks, calculate the smallest width from the bounds of the task
+ inOutConfig.smallestScreenWidthDp = (int) (
+ Math.min(bounds.width(), bounds.height()) / density);
+ } else if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+ // Iterating across all screen orientations, and return the minimum of the task
+ // width taking into account that the bounds might change because the snap
+ // algorithm snaps to a different value
+ getSmallestScreenWidthDpForDockedBounds(bounds);
+ }
+ // otherwise, it will just inherit
+ }
+ }
+
+ if (inOutConfig.orientation == Configuration.ORIENTATION_UNDEFINED) {
+ inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
+ ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
+ }
+ if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
+ // For calculating screen layout, we need to use the non-decor inset screen area for the
+ // calculation for compatibility reasons, i.e. screen area without system bars that
+ // could never go away in Honeycomb.
+ final int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
+ final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+ // We're only overriding LONG, SIZE and COMPAT parts of screenLayout, so we start
+ // override calculation with partial default.
+ // Reducing the screen layout starting from its parent config.
+ final int sl = parentConfig.screenLayout
+ & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
+ final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
+ final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
+ inOutConfig.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
+ }
+ }
+
+ // TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore.
+ void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig,
+ Configuration overrideConfig) {
+ inOutConfig.setTo(overrideConfig);
+
+ Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds();
+ if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) {
+ adjustForMinimalTaskDimensions(outOverrideBounds);
+
+ int windowingMode = overrideConfig.windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = parentConfig.windowConfiguration.getWindowingMode();
+ }
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ // by policy, make sure the window remains within parent
+ fitWithinBounds(outOverrideBounds, parentConfig.windowConfiguration.getBounds());
+ }
+
+ computeConfigResourceOverrides(inOutConfig, outOverrideBounds, parentConfig);
+ }
+ }
+
+ @Override
+ void resolveOverrideConfiguration(Configuration newParentConfig) {
+ computeResolvedOverrideConfiguration(getResolvedOverrideConfiguration(), newParentConfig,
+ getRequestedOverrideConfiguration());
}
Rect updateOverrideConfigurationFromLaunchBounds() {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 7ab4d08..01a5622 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -191,9 +191,7 @@
} else {
mCache.putSnapshot(task, snapshot);
mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
- if (task.getController() != null) {
- task.getController().reportSnapshotChanged(snapshot);
- }
+ task.onSnapshotChanged(snapshot);
}
}
}
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
deleted file mode 100644
index b87b65e..0000000
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ /dev/null
@@ -1,262 +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 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;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.EventLog;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Controller for the task container. This is created by activity manager to link task records to
- * the task container they use in window manager.
- *
- * Test class: {@link TaskWindowContainerControllerTests}
- */
-public class TaskWindowContainerController
- extends WindowContainerController<Task, TaskWindowContainerListener> {
-
- private final int mTaskId;
- private final H mHandler;
-
- public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
- StackWindowController stackController, int userId, Rect bounds, int resizeMode,
- boolean supportsPictureInPicture, boolean toTop, boolean showForAllUsers,
- TaskDescription taskDescription) {
- this(taskId, listener, stackController, userId, bounds, resizeMode,
- supportsPictureInPicture, toTop, showForAllUsers, taskDescription,
- WindowManagerService.getInstance());
- }
-
- public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
- StackWindowController stackController, int userId, Rect bounds, int resizeMode,
- boolean supportsPictureInPicture, boolean toTop, boolean showForAllUsers,
- TaskDescription taskDescription, WindowManagerService service) {
- super(listener, service);
- mTaskId = taskId;
- mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
-
- synchronized (mGlobalLock) {
- if (DEBUG_STACK) Slog.i(TAG_WM, "TaskWindowContainerController: taskId=" + taskId
- + " stack=" + stackController + " bounds=" + bounds);
-
- final TaskStack stack = stackController.mContainer;
- if (stack == null) {
- throw new IllegalArgumentException("TaskWindowContainerController: invalid stack="
- + stackController);
- }
- EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
- final Task task = createTask(taskId, stack, userId, resizeMode,
- supportsPictureInPicture, taskDescription);
- final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
- // We only want to move the parents to the parents if we are creating this task at the
- // top of its stack.
- stack.addTask(task, position, showForAllUsers, toTop /* moveParents */);
- }
- }
-
- @VisibleForTesting
- Task createTask(int taskId, TaskStack stack, int userId, int resizeMode,
- boolean supportsPictureInPicture, TaskDescription taskDescription) {
- return new Task(taskId, stack, userId, mService, resizeMode, supportsPictureInPicture,
- taskDescription, this);
- }
-
- @Override
- public void removeContainer() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: could not find taskId=" + mTaskId);
- return;
- }
- mContainer.removeIfPossible();
- super.removeContainer();
- }
- }
-
- void positionChildAtTop(AppWindowToken aToken) {
- positionChildAt(aToken, POSITION_TOP);
- }
-
- void positionChildAt(AppWindowToken aToken, int position) {
- synchronized (mService.mGlobalLock) {
- if (aToken == null) {
- Slog.w(TAG_WM,
- "Attempted to position of non-existing app");
- return;
- }
-
- final Task task = mContainer;
- if (task == null) {
- throw new IllegalArgumentException("positionChildAt: invalid task=" + this);
- }
- task.positionChildAt(position, aToken, false /* includeParents */);
- }
- }
-
- public void reparent(StackWindowController stackController, int position, boolean moveParents) {
- synchronized (mGlobalLock) {
- if (DEBUG_STACK) Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId
- + " to stack=" + stackController + " at " + position);
- if (mContainer == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM,
- "reparent: could not find taskId=" + mTaskId);
- return;
- }
- final TaskStack stack = stackController.mContainer;
- if (stack == null) {
- throw new IllegalArgumentException("reparent: could not find stack="
- + stackController);
- }
- mContainer.reparent(stack, position, moveParents);
- mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
- }
-
- public void setResizeable(int resizeMode) {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.setResizeable(resizeMode);
- }
- }
- }
-
- public void resize(boolean relayout, boolean forced) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- throw new IllegalArgumentException("resizeTask: taskId " + mTaskId + " not found.");
- }
-
- if (mContainer.setBounds(
- mContainer.getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE
- && relayout) {
- mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
- }
- }
-
- public void getBounds(Rect bounds) {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getBounds(bounds);
- return;
- }
- bounds.setEmpty();
- }
- }
-
- /**
- * Puts this task into docked drag resizing mode. See {@link DragResizeMode}.
- *
- * @param resizing Whether to put the task into drag resize mode.
- */
- public void setTaskDockedResizing(boolean resizing) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "setTaskDockedResizing: taskId " + mTaskId + " not found.");
- return;
- }
- mContainer.setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- }
- }
-
- public void cancelWindowTransition() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "cancelWindowTransition: taskId " + mTaskId + " not found.");
- return;
- }
- mContainer.cancelTaskWindowTransition();
- }
- }
-
- public void setTaskDescription(TaskDescription taskDescription) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "setTaskDescription: taskId " + mTaskId + " not found.");
- return;
- }
- mContainer.setTaskDescription(taskDescription);
- }
- }
-
- public boolean isDragResizing() {
- synchronized (mGlobalLock) {
- return mContainer.isDragResizing();
- }
- }
-
- void reportSnapshotChanged(TaskSnapshot snapshot) {
- mHandler.obtainMessage(H.REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget();
- }
-
- void requestResize(Rect bounds, int resizeMode) {
- mHandler.obtainMessage(H.REQUEST_RESIZE, resizeMode, 0, bounds).sendToTarget();
- }
-
- @Override
- public String toString() {
- return "{TaskWindowContainerController taskId=" + mTaskId + "}";
- }
-
- private static final class H extends Handler {
-
- static final int REPORT_SNAPSHOT_CHANGED = 0;
- static final int REQUEST_RESIZE = 1;
-
- private final WeakReference<TaskWindowContainerController> mController;
-
- H(WeakReference<TaskWindowContainerController> controller, Looper looper) {
- super(looper);
- mController = controller;
- }
-
- @Override
- public void handleMessage(Message msg) {
- final TaskWindowContainerController controller = mController.get();
- final TaskWindowContainerListener listener = (controller != null)
- ? controller.mListener : null;
- if (listener == null) {
- return;
- }
- switch (msg.what) {
- case REPORT_SNAPSHOT_CHANGED:
- listener.onSnapshotChanged((TaskSnapshot) msg.obj);
- break;
- case REQUEST_RESIZE:
- listener.requestResize((Rect) msg.obj, msg.arg1);
- break;
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerListener.java b/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
deleted file mode 100644
index af67de3..0000000
--- a/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
+++ /dev/null
@@ -1,33 +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 android.app.ActivityManager.TaskSnapshot;
-import android.graphics.Rect;
-
-/**
- * Interface used by the creator of {@link TaskWindowContainerController} to listen to changes with
- * the task container.
- */
-public interface TaskWindowContainerListener extends WindowContainerListener {
-
- /** Called when the snapshot of this task has changed. */
- void onSnapshotChanged(TaskSnapshot snapshot);
-
- /** Called when the task container would like its controller to resize. */
- void requestResize(Rect bounds, int resizeMode);
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 240b820..d8225b3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -99,6 +99,11 @@
public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) { }
@Override
+ public int getPasswordComplexity() {
+ return DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+ }
+
+ @Override
public void installUpdateFromFile(ComponentName admin,
ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback listener) {}
@@ -136,4 +141,10 @@
public boolean isUnattendedManagedKiosk() {
return false;
}
+
+ @Override
+ public boolean startViewCalendarEventInManagedProfile(String packageName, long eventId,
+ long start, long end, boolean allDay, int flags) {
+ return false;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bc550dc..7186cdf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.devicepolicy;
import static android.Manifest.permission.BIND_DEVICE_ADMIN;
+import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
@@ -40,10 +41,13 @@
import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL;
import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_SELECTION;
import static android.app.admin.DevicePolicyManager.DELEGATION_ENABLE_SYSTEM_APP;
import static android.app.admin.DevicePolicyManager.DELEGATION_INSTALL_EXISTING_PACKAGE;
import static android.app.admin.DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES;
+import static android.app.admin.DevicePolicyManager.DELEGATION_NETWORK_LOGGING;
import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
+import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_INSTALLATION;
import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
@@ -53,6 +57,7 @@
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
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_OFF;
@@ -113,6 +118,7 @@
import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.NetworkEvent;
import android.app.admin.PasswordMetrics;
@@ -124,6 +130,7 @@
import android.app.backup.IBackupManager;
import android.app.trust.TrustManager;
import android.app.usage.UsageStatsManagerInternal;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -184,6 +191,7 @@
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
+import android.provider.CalendarContract;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsInternal;
import android.provider.Settings;
@@ -361,9 +369,24 @@
DELEGATION_PACKAGE_ACCESS,
DELEGATION_PERMISSION_GRANT,
DELEGATION_INSTALL_EXISTING_PACKAGE,
- DELEGATION_KEEP_UNINSTALLED_PACKAGES
+ DELEGATION_KEEP_UNINSTALLED_PACKAGES,
+ DELEGATION_NETWORK_LOGGING,
+ DELEGATION_CERT_SELECTION,
+ DELEGATION_PACKAGE_INSTALLATION
};
+ // Subset of delegations that can only be delegated by Device Owner.
+ private static final List<String> DEVICE_OWNER_DELEGATIONS = Arrays.asList(new String[] {
+ DELEGATION_NETWORK_LOGGING,
+ DELEGATION_PACKAGE_INSTALLATION
+ });
+
+ // Subset of delegations that only one single package within a given user can hold
+ private static final List<String> EXCLUSIVE_DELEGATIONS = Arrays.asList(new String[] {
+ DELEGATION_NETWORK_LOGGING,
+ DELEGATION_CERT_SELECTION,
+ });
+
/**
* System property whose value is either "true" or "false", indicating whether
* device owner is present.
@@ -4714,6 +4737,22 @@
}
@Override
+ @PasswordComplexity
+ public int getPasswordComplexity() {
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ enforceUserUnlocked(callingUserId);
+ mContext.enforceCallingOrSelfPermission(
+ GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY,
+ "Must have " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY + " permission.");
+
+ synchronized (getLockObject()) {
+ int targetUserId = getCredentialOwner(callingUserId, /* parent= */ false);
+ PasswordMetrics metrics = getUserPasswordMetricsLocked(targetUserId);
+ return metrics == null ? PASSWORD_COMPLEXITY_NONE : metrics.determineComplexity();
+ }
+ }
+
+ @Override
public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
enforceFullCrossUsersPermission(userHandle);
synchronized (getLockObject()) {
@@ -5346,7 +5385,8 @@
@Override
public void enforceCanManageCaCerts(ComponentName who, String callerPackage) {
if (who == null) {
- if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) {
+ if (!isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
+ DELEGATION_CERT_INSTALL)) {
mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
}
} else {
@@ -5765,13 +5805,22 @@
}
Intent intent = new Intent(DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS);
- intent.setComponent(aliasChooser);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID, uid);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_URI, uri);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS, alias);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE, response);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ final ComponentName delegateReceiver;
+ delegateReceiver = resolveDelegateReceiver(DELEGATION_CERT_SELECTION,
+ DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS, caller.getIdentifier());
+
+ if (delegateReceiver != null) {
+ intent.setComponent(delegateReceiver);
+ } else {
+ intent.setComponent(aliasChooser);
+ }
+
final long id = mInjector.binderClearCallingIdentity();
try {
mContext.sendOrderedBroadcastAsUser(intent, caller, null, new BroadcastReceiver() {
@@ -5831,22 +5880,26 @@
*/
@Override
public void setDelegatedScopes(ComponentName who, String delegatePackage,
- List<String> scopes) throws SecurityException {
+ List<String> scopeList) throws SecurityException {
Preconditions.checkNotNull(who, "ComponentName is null");
Preconditions.checkStringNotEmpty(delegatePackage, "Delegate package is null or empty");
- Preconditions.checkCollectionElementsNotNull(scopes, "Scopes");
+ Preconditions.checkCollectionElementsNotNull(scopeList, "Scopes");
// Remove possible duplicates.
- scopes = new ArrayList(new ArraySet(scopes));
+ final ArrayList<String> scopes = new ArrayList(new ArraySet(scopeList));
// Ensure given scopes are valid.
if (scopes.retainAll(Arrays.asList(DELEGATIONS))) {
throw new IllegalArgumentException("Unexpected delegation scopes");
}
-
+ final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS);
// Retrieve the user ID of the calling process.
final int userId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
// Ensure calling process is device/profile owner.
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (hasDoDelegation) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ } else {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
// Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N).
if (shouldCheckIfDelegatePackageIsInstalled(delegatePackage,
getTargetSdk(who.getPackageName(), userId), scopes)) {
@@ -5859,31 +5912,57 @@
// Set the new delegate in user policies.
final DevicePolicyData policy = getUserData(userId);
+ List<String> exclusiveScopes = null;
if (!scopes.isEmpty()) {
policy.mDelegationMap.put(delegatePackage, new ArrayList<>(scopes));
+ exclusiveScopes = new ArrayList<>(scopes);
+ exclusiveScopes.retainAll(EXCLUSIVE_DELEGATIONS);
} else {
// Remove any delegation info if the given scopes list is empty.
policy.mDelegationMap.remove(delegatePackage);
}
+ sendDelegationChangedBroadcast(delegatePackage, scopes, userId);
- // Notify delegate package of updates.
- final Intent intent = new Intent(
- DevicePolicyManager.ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED);
- // Only call receivers registered with Context#registerReceiver (don’t wake delegate).
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- // Limit components this intent resolves to to the delegate package.
- intent.setPackage(delegatePackage);
- // Include the list of delegated scopes as an extra.
- intent.putStringArrayListExtra(DevicePolicyManager.EXTRA_DELEGATION_SCOPES,
- (ArrayList<String>) scopes);
- // Send the broadcast.
- mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+ // If set, remove exclusive scopes from all other delegates
+ if (exclusiveScopes != null && !exclusiveScopes.isEmpty()) {
+ for (Map.Entry<String, List<String>> entry : policy.mDelegationMap.entrySet()) {
+ final String currentPackage = entry.getKey();
+ final List<String> currentScopes = entry.getValue();
+ if (!currentPackage.equals(delegatePackage)) {
+ // Iterate through all other delegates
+ if (currentScopes.removeAll(exclusiveScopes)) {
+ // And if this delegate had some exclusive scopes which are now moved
+ // to the new delegate, notify about its delegation changes.
+ if (currentScopes.isEmpty()) {
+ policy.mDelegationMap.remove(currentPackage);
+ }
+ sendDelegationChangedBroadcast(currentPackage,
+ new ArrayList<>(currentScopes), userId);
+ }
+ }
+ }
+ }
// Persist updates.
saveSettingsLocked(userId);
}
}
+ private void sendDelegationChangedBroadcast(String delegatePackage, ArrayList<String> scopes,
+ int userId) {
+ // Notify delegate package of updates.
+ final Intent intent = new Intent(
+ DevicePolicyManager.ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED);
+ // Only call receivers registered with Context#registerReceiver (don’t wake delegate).
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ // Limit components this intent resolves to to the delegate package.
+ intent.setPackage(delegatePackage);
+ // Include the list of delegated scopes as an extra.
+ intent.putStringArrayListExtra(DevicePolicyManager.EXTRA_DELEGATION_SCOPES, scopes);
+ // Send the broadcast.
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+ }
+
/**
* Get the delegation scopes given to a delegate package by a device owner or profile owner.
*
@@ -5951,17 +6030,59 @@
synchronized (getLockObject()) {
// Ensure calling process is device/profile owner.
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- final DevicePolicyData policy = getUserData(userId);
+ return getDelegatePackagesInternalLocked(scope, userId);
+ }
+ }
- // Create a list to hold the resulting delegate packages.
- final List<String> delegatePackagesWithScope = new ArrayList<>();
- // Add all delegations containing scope to the result list.
- for (int i = 0; i < policy.mDelegationMap.size(); i++) {
- if (policy.mDelegationMap.valueAt(i).contains(scope)) {
- delegatePackagesWithScope.add(policy.mDelegationMap.keyAt(i));
- }
+ private List<String> getDelegatePackagesInternalLocked(String scope, int userId) {
+ final DevicePolicyData policy = getUserData(userId);
+
+ // Create a list to hold the resulting delegate packages.
+ final List<String> delegatePackagesWithScope = new ArrayList<>();
+ // Add all delegations containing scope to the result list.
+ for (int i = 0; i < policy.mDelegationMap.size(); i++) {
+ if (policy.mDelegationMap.valueAt(i).contains(scope)) {
+ delegatePackagesWithScope.add(policy.mDelegationMap.keyAt(i));
}
- return delegatePackagesWithScope;
+ }
+ return delegatePackagesWithScope;
+ }
+
+ /**
+ * Return the ComponentName of the receiver that handles the given broadcast action, from
+ * the app that holds the given delegation capability. If the app defines multiple receivers
+ * with the same intent action filter, will return any one of them nondeterministically.
+ *
+ * @return ComponentName of the receiver or {@null} if none exists.
+ */
+ private ComponentName resolveDelegateReceiver(String scope, String action, int userId) {
+
+ final List<String> delegates;
+ synchronized (getLockObject()) {
+ delegates = getDelegatePackagesInternalLocked(scope, userId);
+ }
+ if (delegates.size() != 1) {
+ Slog.wtf(LOG_TAG, "More than one delegate holds " + scope);
+ return null;
+ }
+ final String pkg = delegates.get(0);
+ Intent intent = new Intent(action);
+ intent.setPackage(pkg);
+ final List<ResolveInfo> receivers;
+ try {
+ receivers = mIPackageManager.queryIntentReceivers(
+ intent, null, 0, userId).getList();
+ } catch (RemoteException e) {
+ return null;
+ }
+ final int count = receivers.size();
+ if (count >= 1) {
+ if (count > 1) {
+ Slog.w(LOG_TAG, pkg + " defines more than one delegate receiver for " + action);
+ }
+ return receivers.get(0).activityInfo.getComponentName();
+ } else {
+ return null;
}
}
@@ -5978,15 +6099,14 @@
* @param scope the delegation scope to be checked.
* @return {@code true} if the calling process is a delegate of {@code scope}.
*/
- private boolean isCallerDelegate(String callerPackage, String scope) {
+ private boolean isCallerDelegate(String callerPackage, int callerUid, String scope) {
Preconditions.checkNotNull(callerPackage, "callerPackage is null");
if (!Arrays.asList(DELEGATIONS).contains(scope)) {
throw new IllegalArgumentException("Unexpected delegation scope: " + scope);
}
// Retrieve the UID and user ID of the calling process.
- final int callingUid = mInjector.binderGetCallingUid();
- final int userId = UserHandle.getUserId(callingUid);
+ final int userId = UserHandle.getUserId(callerUid);
synchronized (getLockObject()) {
// Retrieve user policy data.
final DevicePolicyData policy = getUserData(userId);
@@ -5999,7 +6119,7 @@
final int uid = mInjector.getPackageManager()
.getPackageUidAsUser(callerPackage, userId);
// Return true if the caller is actually callerPackage.
- return uid == callingUid;
+ return uid == callerUid;
} catch (NameNotFoundException e) {
// Ignore.
}
@@ -6024,15 +6144,34 @@
*/
private void enforceCanManageScope(ComponentName who, String callerPackage, int reqPolicy,
String scope) {
+ enforceCanManageScopeOrCheckPermission(who, callerPackage, reqPolicy, scope, null);
+ }
+
+ /**
+ * Throw a security exception if a ComponentName is given and it is not a device/profile owner
+ * OR if the calling process is not a delegate of the given scope and does not hold the
+ * required permission.
+ */
+ private void enforceCanManageScopeOrCheckPermission(@Nullable ComponentName who,
+ @NonNull String callerPackage, int reqPolicy, @NonNull String scope,
+ @Nullable String permission) {
// If a ComponentName is given ensure it is a device or profile owner according to policy.
if (who != null) {
synchronized (getLockObject()) {
getActiveAdminForCallerLocked(who, reqPolicy);
}
- // If no ComponentName is given ensure calling process has scope delegation.
- } else if (!isCallerDelegate(callerPackage, scope)) {
- throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid()
- + " is not a delegate of scope " + scope + ".");
+ } else {
+ // If no ComponentName is given ensure calling process has scope delegation or required
+ // permission
+ if (isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(), scope)) {
+ return;
+ }
+ if (permission == null) {
+ throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid()
+ + " is not a delegate of scope " + scope + ".");
+ } else {
+ mContext.enforceCallingOrSelfPermission(permission, null);
+ }
}
}
@@ -6971,9 +7110,16 @@
}
}
- private void ensureDeviceOwnerAndAllUsersAffiliated(ComponentName who) throws SecurityException {
+ private void ensureDeviceOwnerAndAllUsersAffiliated(ComponentName who)
+ throws SecurityException {
synchronized (getLockObject()) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+ ensureAllUsersAffiliated();
+ }
+
+ private void ensureAllUsersAffiliated() throws SecurityException {
+ synchronized (getLockObject()) {
if (!areAllUsersAffiliatedWithDeviceLocked()) {
throw new SecurityException("Not all users are affiliated.");
}
@@ -7032,14 +7178,22 @@
}
void sendDeviceOwnerCommand(String action, Bundle extras) {
- int deviceOwnerUserId;
- ComponentName deviceOwnerComponent;
+ final int deviceOwnerUserId;
synchronized (getLockObject()) {
deviceOwnerUserId = mOwners.getDeviceOwnerUserId();
- deviceOwnerComponent = mOwners.getDeviceOwnerComponent();
}
- sendActiveAdminCommand(action, extras, deviceOwnerUserId,
- deviceOwnerComponent);
+
+ ComponentName receiverComponent = null;
+ if (action.equals(DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE)) {
+ receiverComponent = resolveDelegateReceiver(DELEGATION_NETWORK_LOGGING, action,
+ deviceOwnerUserId);
+ }
+ if (receiverComponent == null) {
+ synchronized (getLockObject()) {
+ receiverComponent = mOwners.getDeviceOwnerComponent();
+ }
+ }
+ sendActiveAdminCommand(action, extras, deviceOwnerUserId, receiverComponent);
}
private void sendProfileOwnerCommand(String action, Bundle extras, int userHandle) {
@@ -8496,7 +8650,8 @@
@Override
public boolean isCallerApplicationRestrictionsManagingPackage(String callerPackage) {
- return isCallerDelegate(callerPackage, DELEGATION_APP_RESTRICTIONS);
+ return isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
+ DELEGATION_APP_RESTRICTIONS);
}
@Override
@@ -10715,6 +10870,24 @@
}
@Override
+ public boolean canSilentlyInstallPackage(String callerPackage, int callerUid) {
+ if (callerPackage == null) {
+ return false;
+ }
+ if (isUserAffiliatedWithDevice(UserHandle.getUserId(callerUid))
+ && isActiveAdminWithPolicy(callerUid,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) {
+ // device owner or a profile owner affiliated with the device owner
+ return true;
+ }
+ if (DevicePolicyManagerService.this.isCallerDelegate(callerPackage, callerUid,
+ DELEGATION_PACKAGE_INSTALLATION)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public void reportSeparateProfileChallengeChanged(@UserIdInt int userId) {
synchronized (getLockObject()) {
updateMaximumTimeToLockLocked(userId);
@@ -12507,13 +12680,14 @@
}
@Override
- public void setNetworkLoggingEnabled(ComponentName admin, boolean enabled) {
+ public void setNetworkLoggingEnabled(@Nullable ComponentName admin,
+ @NonNull String packageName, boolean enabled) {
if (!mHasFeature) {
return;
}
synchronized (getLockObject()) {
- Preconditions.checkNotNull(admin);
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ enforceCanManageScope(admin, packageName, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+ DELEGATION_NETWORK_LOGGING);
if (enabled == isNetworkLoggingEnabledInternalLocked()) {
// already in the requested state
@@ -12614,12 +12788,15 @@
}
@Override
- public boolean isNetworkLoggingEnabled(ComponentName admin) {
+ public boolean isNetworkLoggingEnabled(@Nullable ComponentName admin,
+ @NonNull String packageName) {
if (!mHasFeature) {
return false;
}
synchronized (getLockObject()) {
- enforceDeviceOwnerOrManageUsers();
+ enforceCanManageScopeOrCheckPermission(admin, packageName,
+ DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, DELEGATION_NETWORK_LOGGING,
+ android.Manifest.permission.MANAGE_USERS);
return isNetworkLoggingEnabledInternalLocked();
}
}
@@ -12637,12 +12814,14 @@
* @see NetworkLoggingHandler#MAX_EVENTS_PER_BATCH
*/
@Override
- public List<NetworkEvent> retrieveNetworkLogs(ComponentName admin, long batchToken) {
+ public List<NetworkEvent> retrieveNetworkLogs(@Nullable ComponentName admin,
+ @NonNull String packageName, long batchToken) {
if (!mHasFeature) {
return null;
}
- Preconditions.checkNotNull(admin);
- ensureDeviceOwnerAndAllUsersAffiliated(admin);
+ enforceCanManageScope(admin, packageName, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+ DELEGATION_NETWORK_LOGGING);
+ ensureAllUsersAffiliated();
synchronized (getLockObject()) {
if (mNetworkLogger == null
@@ -13658,4 +13837,59 @@
private PowerManagerInternal getPowerManagerInternal() {
return mInjector.getPowerManagerInternal();
}
+
+ @Override
+ public boolean startViewCalendarEventInManagedProfile(String packageName, long eventId,
+ long start, long end, boolean allDay, int flags) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkStringNotEmpty(packageName, "Package name is empty");
+
+ final int callingUid = mInjector.binderGetCallingUid();
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ if (!isCallingFromPackage(packageName, callingUid)) {
+ throw new SecurityException("Input package name doesn't align with actual "
+ + "calling package.");
+ }
+ final long identity = mInjector.binderClearCallingIdentity();
+ try {
+ final int workProfileUserId = getManagedUserId(callingUserId);
+ if (workProfileUserId < 0) {
+ return false;
+ }
+ if (!isPackageAllowedToAccessCalendarForUser(packageName, workProfileUserId)) {
+ Log.d(LOG_TAG, String.format("Package %s is not allowed to access cross-profile"
+ + "calendar APIs", packageName));
+ return false;
+ }
+ final Intent intent = new Intent(CalendarContract.ACTION_VIEW_WORK_CALENDAR_EVENT);
+ intent.setPackage(packageName);
+ intent.putExtra(CalendarContract.EXTRA_EVENT_ID, eventId);
+ intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, start);
+ intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end);
+ intent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, allDay);
+ intent.setFlags(flags);
+ try {
+ mContext.startActivityAsUser(intent, UserHandle.of(workProfileUserId));
+ } catch (ActivityNotFoundException e) {
+ Log.e(LOG_TAG, "View event activity not found", e);
+ return false;
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(identity);
+ }
+ return true;
+ }
+
+ private boolean isCallingFromPackage(String packageName, int callingUid) {
+ try {
+ final int packageUid = mInjector.getPackageManager().getPackageUidAsUser(
+ packageName, UserHandle.getUserId(callingUid));
+ return packageUid == callingUid;
+ } catch (NameNotFoundException e) {
+ Log.d(LOG_TAG, "Calling package not found", e);
+ return false;
+ }
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
index 7910598..d8a875d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
+import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.admin.StartInstallingUpdateCallback;
import android.content.Context;
@@ -26,6 +27,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.util.Log;
import java.io.File;
@@ -132,6 +134,10 @@
protected void notifyCallbackOnError(int errorCode, String errorMessage) {
cleanupUpdateFile();
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.INSTALL_SYSTEM_UPDATE_ERROR)
+ .setInt(errorCode)
+ .write();
try {
mCallback.onStartInstallingUpdateError(errorCode, errorMessage);
} catch (RemoteException e) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
new file mode 100644
index 0000000..8e78a56
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.job.controllers;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertFalse;
+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.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkPolicyManager;
+import android.os.Build;
+import android.os.SystemClock;
+import android.util.DataUnit;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobSchedulerService.Constants;
+import com.android.server.net.NetworkPolicyManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.time.Clock;
+import java.time.ZoneOffset;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ConnectivityControllerTest {
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private ConnectivityManager mConnManager;
+ @Mock
+ private NetworkPolicyManager mNetPolicyManager;
+ @Mock
+ private NetworkPolicyManagerInternal mNetPolicyManagerInternal;
+ @Mock
+ private JobSchedulerService mService;
+
+ private Constants mConstants;
+
+ private static final int UID_RED = 10001;
+ private static final int UID_BLUE = 10002;
+
+ @Before
+ public void setUp() throws Exception {
+ // Assume all packages are current SDK
+ final PackageManagerInternal pm = mock(PackageManagerInternal.class);
+ when(pm.getPackageTargetSdkVersion(anyString()))
+ .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, pm);
+
+ LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
+ LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);
+
+ // Freeze the clocks at this moment in time
+ JobSchedulerService.sSystemClock =
+ Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sUptimeMillisClock =
+ Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+
+ // Assume default constants for now
+ mConstants = new Constants();
+
+ // Get our mocks ready
+ when(mContext.getSystemServiceName(ConnectivityManager.class))
+ .thenReturn(Context.CONNECTIVITY_SERVICE);
+ when(mContext.getSystemService(ConnectivityManager.class))
+ .thenReturn(mConnManager);
+ when(mContext.getSystemServiceName(NetworkPolicyManager.class))
+ .thenReturn(Context.NETWORK_POLICY_SERVICE);
+ when(mContext.getSystemService(NetworkPolicyManager.class))
+ .thenReturn(mNetPolicyManager);
+ when(mService.getTestableContext()).thenReturn(mContext);
+ when(mService.getLock()).thenReturn(mService);
+ when(mService.getConstants()).thenReturn(mConstants);
+ }
+
+ @Test
+ public void testInsane() throws Exception {
+ final Network net = new Network(101);
+ final JobInfo.Builder job = createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+
+ // Slow network is too slow
+ assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net,
+ createCapabilities().setLinkUpstreamBandwidthKbps(1)
+ .setLinkDownstreamBandwidthKbps(1), mConstants));
+ // Fast network looks great
+ assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), net,
+ createCapabilities().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(1024), mConstants));
+ }
+
+ @Test
+ public void testCongestion() throws Exception {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final JobInfo.Builder job = createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+ final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
+ final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
+
+ // Uncongested network is whenever
+ {
+ final Network net = new Network(101);
+ final NetworkCapabilities caps = createCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED);
+ assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
+ }
+
+ // Congested network is more selective
+ {
+ final Network net = new Network(101);
+ final NetworkCapabilities caps = createCapabilities();
+ assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
+ }
+ }
+
+ @Test
+ public void testRelaxed() throws Exception {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final JobInfo.Builder job = createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
+ final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
+ final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
+
+ job.setIsPrefetch(true);
+ final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000);
+ final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000);
+
+ // Unmetered network is whenever
+ {
+ final Network net = new Network(101);
+ final NetworkCapabilities caps = createCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .addCapability(NET_CAPABILITY_NOT_METERED);
+ assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
+ }
+
+ // Metered network is only when prefetching and late
+ {
+ final Network net = new Network(101);
+ final NetworkCapabilities caps = createCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED);
+ assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
+ assertFalse(ConnectivityController.isSatisfied(late, net, caps, mConstants));
+ assertFalse(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
+ assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
+ }
+ }
+
+ @Test
+ public void testUpdates() throws Exception {
+ final ArgumentCaptor<NetworkCallback> callback = ArgumentCaptor
+ .forClass(NetworkCallback.class);
+ doNothing().when(mConnManager).registerNetworkCallback(any(), callback.capture());
+
+ final ConnectivityController controller = new ConnectivityController(mService);
+
+ final Network meteredNet = new Network(101);
+ final NetworkCapabilities meteredCaps = createCapabilities();
+ final Network unmeteredNet = new Network(202);
+ final NetworkCapabilities unmeteredCaps = createCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_METERED);
+
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+ // Pretend we're offline when job is added
+ {
+ reset(mConnManager);
+ answerNetwork(UID_RED, null, null);
+ answerNetwork(UID_BLUE, null, null);
+
+ controller.maybeStartTrackingJobLocked(red, null);
+ controller.maybeStartTrackingJobLocked(blue, null);
+
+ assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+
+ // Metered network
+ {
+ reset(mConnManager);
+ answerNetwork(UID_RED, meteredNet, meteredCaps);
+ answerNetwork(UID_BLUE, meteredNet, meteredCaps);
+
+ callback.getValue().onCapabilitiesChanged(meteredNet, meteredCaps);
+
+ assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+
+ // Unmetered network background
+ {
+ reset(mConnManager);
+ answerNetwork(UID_RED, meteredNet, meteredCaps);
+ answerNetwork(UID_BLUE, meteredNet, meteredCaps);
+
+ callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
+
+ assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+
+ // Lost metered network
+ {
+ reset(mConnManager);
+ answerNetwork(UID_RED, unmeteredNet, unmeteredCaps);
+ answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
+
+ callback.getValue().onLost(meteredNet);
+
+ assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+
+ // Specific UID was blocked
+ {
+ reset(mConnManager);
+ answerNetwork(UID_RED, null, null);
+ answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
+
+ callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
+
+ assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+ }
+
+ @Test
+ public void testRequestStandbyExceptionLocked() {
+ final ConnectivityController controller = new ConnectivityController(mService);
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+ InOrder inOrder = inOrder(mNetPolicyManagerInternal);
+
+ controller.requestStandbyExceptionLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(true));
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ // Whitelisting doesn't need to be requested again.
+ controller.requestStandbyExceptionLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+
+ controller.requestStandbyExceptionLocked(blue);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_BLUE), eq(true));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ }
+
+ @Test
+ public void testWouldBeReadyWithConnectivityLocked() {
+ final ConnectivityController controller = spy(new ConnectivityController(mService));
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+
+ doReturn(false).when(controller).isNetworkAvailable(any());
+ assertFalse(controller.wouldBeReadyWithConnectivityLocked(red));
+
+ doReturn(true).when(controller).isNetworkAvailable(any());
+ doReturn(false).when(controller).wouldBeReadyWithConstraintLocked(any(),
+ eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(controller.wouldBeReadyWithConnectivityLocked(red));
+
+ doReturn(true).when(controller).isNetworkAvailable(any());
+ doReturn(true).when(controller).wouldBeReadyWithConstraintLocked(any(),
+ eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(controller.wouldBeReadyWithConnectivityLocked(red));
+ }
+
+ @Test
+ public void testEvaluateStateLocked_HeartbeatsOn() {
+ mConstants.USE_HEARTBEATS = true;
+ final ConnectivityController controller = new ConnectivityController(mService);
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+
+ controller.evaluateStateLocked(red);
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ }
+
+ @Test
+ public void testEvaluateStateLocked_JobWithoutConnectivity() {
+ mConstants.USE_HEARTBEATS = false;
+ final ConnectivityController controller = new ConnectivityController(mService);
+ final JobStatus red = createJobStatus(createJob().setMinimumLatency(1));
+
+ controller.evaluateStateLocked(red);
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ }
+
+ @Test
+ public void testEvaluateStateLocked_JobWouldBeReady() {
+ mConstants.USE_HEARTBEATS = false;
+ final ConnectivityController controller = spy(new ConnectivityController(mService));
+ doReturn(true).when(controller).wouldBeReadyWithConnectivityLocked(any());
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+ InOrder inOrder = inOrder(mNetPolicyManagerInternal);
+
+ controller.evaluateStateLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(true));
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ // Whitelisting doesn't need to be requested again.
+ controller.evaluateStateLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+
+ controller.evaluateStateLocked(blue);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_BLUE), eq(true));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ }
+
+ @Test
+ public void testEvaluateStateLocked_JobWouldNotBeReady() {
+ mConstants.USE_HEARTBEATS = false;
+ final ConnectivityController controller = spy(new ConnectivityController(mService));
+ doReturn(false).when(controller).wouldBeReadyWithConnectivityLocked(any());
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+ InOrder inOrder = inOrder(mNetPolicyManagerInternal);
+
+ controller.evaluateStateLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+
+ // Test that a currently whitelisted uid is now removed.
+ controller.requestStandbyExceptionLocked(blue);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_BLUE), eq(true));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ controller.evaluateStateLocked(blue);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_BLUE), eq(false));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ }
+
+ @Test
+ public void testReevaluateStateLocked() {
+ mConstants.USE_HEARTBEATS = false;
+ final ConnectivityController controller = spy(new ConnectivityController(mService));
+ final JobStatus redOne = createJobStatus(createJob(1)
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus redTwo = createJobStatus(createJob(2)
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+ controller.maybeStartTrackingJobLocked(redOne, null);
+ controller.maybeStartTrackingJobLocked(redTwo, null);
+ controller.maybeStartTrackingJobLocked(blue, null);
+
+ InOrder inOrder = inOrder(mNetPolicyManagerInternal);
+ controller.requestStandbyExceptionLocked(redOne);
+ controller.requestStandbyExceptionLocked(redTwo);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(true));
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+
+ // Make sure nothing happens if an exception hasn't been requested.
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
+ controller.reevaluateStateLocked(UID_BLUE);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
+
+ // Make sure a job that isn't being tracked doesn't cause issues.
+ assertFalse(controller.isStandbyExceptionRequestedLocked(12345));
+ controller.reevaluateStateLocked(12345);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(12345), anyBoolean());
+
+ // Both jobs would still be ready. Exception should not be revoked.
+ doReturn(true).when(controller).wouldBeReadyWithConnectivityLocked(any());
+ controller.reevaluateStateLocked(UID_RED);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+
+ // One job is still ready. Exception should not be revoked.
+ doReturn(true).when(controller).wouldBeReadyWithConnectivityLocked(eq(redOne));
+ doReturn(false).when(controller).wouldBeReadyWithConnectivityLocked(eq(redTwo));
+ controller.reevaluateStateLocked(UID_RED);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+
+ // Both jobs are not ready. Exception should be revoked.
+ doReturn(false).when(controller).wouldBeReadyWithConnectivityLocked(any());
+ controller.reevaluateStateLocked(UID_RED);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(false));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ }
+
+ @Test
+ public void testMaybeRevokeStandbyExceptionLocked() {
+ final ConnectivityController controller = new ConnectivityController(mService);
+ final JobStatus red = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+ final JobStatus blue = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+ InOrder inOrder = inOrder(mNetPolicyManagerInternal);
+ controller.requestStandbyExceptionLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(true));
+
+ // Try revoking for blue instead of red. Red should still have an exception requested.
+ controller.maybeRevokeStandbyExceptionLocked(blue);
+ inOrder.verify(mNetPolicyManagerInternal, never())
+ .setAppIdleWhitelist(anyInt(), anyBoolean());
+ assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
+
+ // Now revoke for red.
+ controller.maybeRevokeStandbyExceptionLocked(red);
+ inOrder.verify(mNetPolicyManagerInternal, times(1))
+ .setAppIdleWhitelist(eq(UID_RED), eq(false));
+ assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
+ }
+
+ private void answerNetwork(int uid, Network net, NetworkCapabilities caps) {
+ when(mConnManager.getActiveNetworkForUid(eq(uid))).thenReturn(net);
+ when(mConnManager.getNetworkCapabilities(eq(net))).thenReturn(caps);
+ if (net != null) {
+ final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, null, null);
+ ni.setDetailedState(DetailedState.CONNECTED, null, null);
+ when(mConnManager.getNetworkInfoForUid(eq(net), eq(uid), anyBoolean())).thenReturn(ni);
+ }
+ }
+
+ private static NetworkCapabilities createCapabilities() {
+ return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_VALIDATED);
+ }
+
+ private static JobInfo.Builder createJob() {
+ return createJob(101);
+ }
+
+ private static JobInfo.Builder createJob(int jobId) {
+ return new JobInfo.Builder(jobId, new ComponentName("foo", "bar"));
+ }
+
+ private static JobStatus createJobStatus(JobInfo.Builder job) {
+ return createJobStatus(job, android.os.Process.NOBODY_UID, 0, Long.MAX_VALUE);
+ }
+
+ private static JobStatus createJobStatus(JobInfo.Builder job, int uid) {
+ return createJobStatus(job, uid, 0, Long.MAX_VALUE);
+ }
+
+ private static JobStatus createJobStatus(JobInfo.Builder job,
+ long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
+ return createJobStatus(job, android.os.Process.NOBODY_UID,
+ earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
+ }
+
+ private static JobStatus createJobStatus(JobInfo.Builder job, int uid,
+ long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
+ return new JobStatus(job.build(), uid, null, -1, 0, 0, null,
+ earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index b2ec835..3fc4e89 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -87,7 +87,6 @@
private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
private static final String TAG_CLEANUP = "*job.cleanup*";
private static final String TAG_QUOTA_CHECK = "*job.quota_check*";
- private static final long IN_QUOTA_BUFFER_MILLIS = 30 * SECOND_IN_MILLIS;
private static final int CALLING_UID = 1000;
private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
private static final int SOURCE_USER_ID = 0;
@@ -365,7 +364,8 @@
final long end = now - (2 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
// Counting backwards, the quota will come back one minute before the end.
final long expectedAlarmTime =
- end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + IN_QUOTA_BUFFER_MILLIS;
+ end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS
+ + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
mQuotaController.saveTimingSession(0, "com.android.test",
new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1));
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
@@ -415,7 +415,8 @@
// Test with timing sessions in window but still in quota.
final long start = now - (6 * HOUR_IN_MILLIS);
- final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + IN_QUOTA_BUFFER_MILLIS;
+ final long expectedAlarmTime =
+ start + 8 * HOUR_IN_MILLIS + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
@@ -468,7 +469,8 @@
// Counting backwards, the first minute in the session is over the allowed time, so it
// needs to be excluded.
final long expectedAlarmTime =
- start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS + IN_QUOTA_BUFFER_MILLIS;
+ start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS
+ + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
@@ -529,19 +531,22 @@
// And down from there.
final long expectedWorkingAlarmTime =
- outOfQuotaTime + (2 * HOUR_IN_MILLIS) + IN_QUOTA_BUFFER_MILLIS;
+ outOfQuotaTime + (2 * HOUR_IN_MILLIS)
+ + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
final long expectedFrequentAlarmTime =
- outOfQuotaTime + (8 * HOUR_IN_MILLIS) + IN_QUOTA_BUFFER_MILLIS;
+ outOfQuotaTime + (8 * HOUR_IN_MILLIS)
+ + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
final long expectedRareAlarmTime =
- outOfQuotaTime + (24 * HOUR_IN_MILLIS) + IN_QUOTA_BUFFER_MILLIS;
+ outOfQuotaTime + (24 * HOUR_IN_MILLIS)
+ + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX);
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/StateControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/StateControllerTest.java
new file mode 100644
index 0000000..db69242
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/StateControllerTest.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.server.job.controllers;
+
+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.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+
+import android.app.AlarmManager;
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.os.SystemClock;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobSchedulerService.Constants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.time.Clock;
+import java.time.ZoneOffset;
+import java.util.function.Predicate;
+
+@RunWith(AndroidJUnit4.class)
+public class StateControllerTest {
+ private static final long SECOND_IN_MILLIS = 1000L;
+ private static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
+ private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
+ private static final int CALLING_UID = 1000;
+ private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
+ private static final int SOURCE_USER_ID = 0;
+
+ private Constants mConstants;
+ private StateController mStateController;
+
+ private MockitoSession mMockingSession;
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private JobSchedulerService mJobSchedulerService;
+
+ private class TestStateController extends StateController {
+ TestStateController(JobSchedulerService service) {
+ super(service);
+ }
+
+ public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
+ }
+
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+ boolean forUpdate) {
+ }
+
+ public void dumpControllerStateLocked(IndentingPrintWriter pw,
+ Predicate<JobStatus> predicate) {
+ }
+
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
+ Predicate<JobStatus> predicate) {
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+ // Use default constants for now.
+ mConstants = new Constants();
+
+ // Called in StateController constructor.
+ when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+ when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+ when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+ // Called in QuotaController constructor.
+ // Used in JobStatus.
+ doReturn(mock(PackageManagerInternal.class))
+ .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
+ // Freeze the clocks at this moment in time
+ JobSchedulerService.sSystemClock =
+ Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sUptimeMillisClock =
+ Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+
+ // Initialize real objects.
+ mStateController = new TestStateController(mJobSchedulerService);
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ private JobStatus createJobStatus(String testTag, int jobId) {
+ JobInfo jobInfo = new JobInfo.Builder(jobId,
+ new ComponentName(mContext, "TestQuotaJobService"))
+ .setMinimumLatency(Math.abs(jobId) + 1)
+ .build();
+ return JobStatus.createFromJobInfo(
+ jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+ }
+
+ @Test
+ public void testWouldBeReadyWithConstraintLocked() {
+ JobStatus job = spy(createJobStatus("testWouldBeReadyWithConstraintLocked", 1));
+
+ when(job.wouldBeReadyWithConstraint(anyInt())).thenReturn(false);
+ assertFalse(mStateController.wouldBeReadyWithConstraintLocked(job, 1));
+
+ when(job.wouldBeReadyWithConstraint(anyInt())).thenReturn(true);
+ when(mJobSchedulerService.areComponentsInPlaceLocked(job)).thenReturn(false);
+ assertFalse(mStateController.wouldBeReadyWithConstraintLocked(job, 1));
+
+ when(job.wouldBeReadyWithConstraint(anyInt())).thenReturn(true);
+ when(mJobSchedulerService.areComponentsInPlaceLocked(job)).thenReturn(true);
+ assertTrue(mStateController.wouldBeReadyWithConstraintLocked(job, 1));
+ }
+}
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 c3a0dda..729fac5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -21,6 +21,9 @@
import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.os.UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY;
@@ -48,6 +51,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
+import static org.testng.Assert.assertThrows;
import android.Manifest.permission;
import android.annotation.RawRes;
@@ -5133,6 +5137,71 @@
});
}
+ public void testGetPasswordComplexity_securityExceptionIfParentInstance() {
+ assertThrows(SecurityException.class,
+ () -> new DevicePolicyManagerTestable(
+ mServiceContext,
+ dpms,
+ /* parentInstance= */ true)
+ .getPasswordComplexity());
+ }
+
+ public void testGetPasswordComplexity_illegalStateExceptionIfLocked() {
+ when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(false);
+ assertThrows(IllegalStateException.class, () -> dpm.getPasswordComplexity());
+ }
+
+ public void testGetPasswordComplexity_securityExceptionWithoutPermissions() {
+ when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(true);
+ assertThrows(SecurityException.class, () -> dpm.getPasswordComplexity());
+ }
+
+
+ public void testGetPasswordComplexity_currentUserNoPassword() {
+ when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(true);
+ mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+ when(getServices().userManager.getCredentialOwnerProfile(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(DpmMockContext.CALLER_USER_HANDLE);
+
+ assertEquals(PASSWORD_COMPLEXITY_NONE, dpm.getPasswordComplexity());
+ }
+
+ public void testGetPasswordComplexity_currentUserHasPassword() {
+ when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(true);
+ mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+ when(getServices().userManager.getCredentialOwnerProfile(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(DpmMockContext.CALLER_USER_HANDLE);
+ dpms.mUserPasswordMetrics.put(
+ DpmMockContext.CALLER_USER_HANDLE,
+ PasswordMetrics.computeForPassword("asdf"));
+
+ assertEquals(PASSWORD_COMPLEXITY_MEDIUM, dpm.getPasswordComplexity());
+ }
+
+ public void testGetPasswordComplexity_unifiedChallengeReturnsParentUserPassword() {
+ when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(true);
+ mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+
+ UserInfo parentUser = new UserInfo();
+ parentUser.id = DpmMockContext.CALLER_USER_HANDLE + 10;
+ when(getServices().userManager.getCredentialOwnerProfile(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(parentUser.id);
+
+ dpms.mUserPasswordMetrics.put(
+ DpmMockContext.CALLER_USER_HANDLE,
+ PasswordMetrics.computeForPassword("asdf"));
+ dpms.mUserPasswordMetrics.put(
+ parentUser.id,
+ PasswordMetrics.computeForPassword("parentUser"));
+
+ assertEquals(PASSWORD_COMPLEXITY_HIGH, dpm.getPasswordComplexity());
+ }
+
private void configureProfileOwnerForDeviceIdAccess(ComponentName who, int userId) {
final long ident = mServiceContext.binder.clearCallingIdentity();
mServiceContext.binder.callingUid =
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
index 3da61d6..4982d6e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
@@ -26,7 +26,12 @@
public DevicePolicyManagerTestable(DpmMockContext context,
DevicePolicyManagerServiceTestable dpms) {
- super(context, dpms, /* parentInstance = */ false);
+ this(context, dpms, /* parentInstance= */ false);
+ }
+
+ public DevicePolicyManagerTestable(DpmMockContext context,
+ DevicePolicyManagerServiceTestable dpms, boolean parentInstance) {
+ super(context, dpms, parentInstance);
this.dpms = dpms;
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index b421280..e9bfa8f 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -18,11 +18,21 @@
import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.Curve;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayViewport;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.input.InputManagerInternal;
import android.os.Handler;
@@ -31,9 +41,9 @@
import android.view.DisplayInfo;
import android.view.SurfaceControl;
-import androidx.test.runner.AndroidJUnit4;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -49,17 +59,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Arrays;
import java.util.List;
-
-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.junit.Assert.fail;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.mock;
+import java.util.stream.LongStream;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -397,6 +398,43 @@
displayManager.validateBrightnessConfiguration(null);
}
+ /**
+ * Tests that collection of display color sampling results are sensible.
+ */
+ @Test
+ public void testDisplayedContentSampling() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ registerDefaultDisplays(displayManager);
+
+ DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(0);
+ assertNotNull(ddi);
+
+ DisplayedContentSamplingAttributes attr =
+ displayManager.getDisplayedContentSamplingAttributesInternal(0);
+ if (attr == null) return; //sampling not supported on device, skip remainder of test.
+
+ boolean enabled = displayManager.setDisplayedContentSamplingEnabledInternal(0, true, 0, 0);
+ assertTrue(!enabled);
+
+ displayManager.setDisplayedContentSamplingEnabledInternal(0, false, 0, 0);
+ DisplayedContentSample sample = displayManager.getDisplayedContentSampleInternal(0, 0, 0);
+ assertNotNull(sample);
+
+ long numPixels = ddi.width * ddi.height * sample.getNumFrames();
+ long[] samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL0);
+ assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+
+ samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL1);
+ assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+
+ samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL2);
+ assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+
+ samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL3);
+ assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+ }
+
private void registerDefaultDisplays(DisplayManagerService displayManager) {
Handler handler = displayManager.getDisplayHandler();
// Would prefer to call displayManager.onStart() directly here but it performs binderService
diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
deleted file mode 100644
index 5b59e60..0000000
--- a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ /dev/null
@@ -1,313 +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.job.controllers;
-
-import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.when;
-
-import android.app.job.JobInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManagerInternal;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkPolicyManager;
-import android.os.Build;
-import android.os.SystemClock;
-import android.util.DataUnit;
-
-import com.android.server.LocalServices;
-import com.android.server.job.JobSchedulerService;
-import com.android.server.job.JobSchedulerService.Constants;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.time.Clock;
-import java.time.ZoneOffset;
-
-@RunWith(MockitoJUnitRunner.class)
-public class ConnectivityControllerTest {
-
- @Mock private Context mContext;
- @Mock private ConnectivityManager mConnManager;
- @Mock private NetworkPolicyManager mNetPolicyManager;
- @Mock private JobSchedulerService mService;
-
- private Constants mConstants;
-
- private static final int UID_RED = 10001;
- private static final int UID_BLUE = 10002;
-
- @Before
- public void setUp() throws Exception {
- // Assume all packages are current SDK
- final PackageManagerInternal pm = mock(PackageManagerInternal.class);
- when(pm.getPackageTargetSdkVersion(anyString()))
- .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT);
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, pm);
-
- // Freeze the clocks at this moment in time
- JobSchedulerService.sSystemClock =
- Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
- JobSchedulerService.sUptimeMillisClock =
- Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
- JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
-
- // Assume default constants for now
- mConstants = new Constants();
-
- // Get our mocks ready
- when(mContext.getSystemServiceName(ConnectivityManager.class))
- .thenReturn(Context.CONNECTIVITY_SERVICE);
- when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
- .thenReturn(mConnManager);
- when(mContext.getSystemServiceName(NetworkPolicyManager.class))
- .thenReturn(Context.NETWORK_POLICY_SERVICE);
- when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
- .thenReturn(mNetPolicyManager);
- when(mService.getTestableContext()).thenReturn(mContext);
- when(mService.getLock()).thenReturn(mService);
- when(mService.getConstants()).thenReturn(mConstants);
- }
-
- @Test
- public void testInsane() throws Exception {
- final Network net = new Network(101);
- final JobInfo.Builder job = createJob()
- .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
-
- // Slow network is too slow
- assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1)
- .setLinkDownstreamBandwidthKbps(1), mConstants));
- // Fast network looks great
- assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1024)
- .setLinkDownstreamBandwidthKbps(1024), mConstants));
- }
-
- @Test
- public void testCongestion() throws Exception {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- final JobInfo.Builder job = createJob()
- .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
- final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
- final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
-
- // Uncongested network is whenever
- {
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_CONGESTED);
- assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
- }
-
- // Congested network is more selective
- {
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities();
- assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
- }
- }
-
- @Test
- public void testRelaxed() throws Exception {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- final JobInfo.Builder job = createJob()
- .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
- final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
- final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
-
- job.setIsPrefetch(true);
- final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000);
- final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000);
-
- // Unmetered network is whenever
- {
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_CONGESTED)
- .addCapability(NET_CAPABILITY_NOT_METERED);
- assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
- }
-
- // Metered network is only when prefetching and late
- {
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_CONGESTED);
- assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
- assertFalse(ConnectivityController.isSatisfied(late, net, caps, mConstants));
- assertFalse(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
- assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
- }
- }
-
- @Test
- public void testUpdates() throws Exception {
- final ArgumentCaptor<NetworkCallback> callback = ArgumentCaptor
- .forClass(NetworkCallback.class);
- doNothing().when(mConnManager).registerNetworkCallback(any(), callback.capture());
-
- final ConnectivityController controller = new ConnectivityController(mService);
-
- final Network meteredNet = new Network(101);
- final NetworkCapabilities meteredCaps = createCapabilities();
- final Network unmeteredNet = new Network(202);
- final NetworkCapabilities unmeteredCaps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_METERED);
-
- final JobStatus red = createJobStatus(createJob()
- .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
- final JobStatus blue = createJobStatus(createJob()
- .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
-
- // Pretend we're offline when job is added
- {
- reset(mConnManager);
- answerNetwork(UID_RED, null, null);
- answerNetwork(UID_BLUE, null, null);
-
- controller.maybeStartTrackingJobLocked(red, null);
- controller.maybeStartTrackingJobLocked(blue, null);
-
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- }
-
- // Metered network
- {
- reset(mConnManager);
- answerNetwork(UID_RED, meteredNet, meteredCaps);
- answerNetwork(UID_BLUE, meteredNet, meteredCaps);
-
- callback.getValue().onCapabilitiesChanged(meteredNet, meteredCaps);
-
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- }
-
- // Unmetered network background
- {
- reset(mConnManager);
- answerNetwork(UID_RED, meteredNet, meteredCaps);
- answerNetwork(UID_BLUE, meteredNet, meteredCaps);
-
- callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
-
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- }
-
- // Lost metered network
- {
- reset(mConnManager);
- answerNetwork(UID_RED, unmeteredNet, unmeteredCaps);
- answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
-
- callback.getValue().onLost(meteredNet);
-
- assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- }
-
- // Specific UID was blocked
- {
- reset(mConnManager);
- answerNetwork(UID_RED, null, null);
- answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
-
- callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
-
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- }
- }
-
- private void answerNetwork(int uid, Network net, NetworkCapabilities caps) {
- when(mConnManager.getActiveNetworkForUid(eq(uid))).thenReturn(net);
- when(mConnManager.getNetworkCapabilities(eq(net))).thenReturn(caps);
- if (net != null) {
- final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, null, null);
- ni.setDetailedState(DetailedState.CONNECTED, null, null);
- when(mConnManager.getNetworkInfoForUid(eq(net), eq(uid), anyBoolean())).thenReturn(ni);
- }
- }
-
- private static NetworkCapabilities createCapabilities() {
- return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
- .addCapability(NET_CAPABILITY_VALIDATED);
- }
-
- private static JobInfo.Builder createJob() {
- return new JobInfo.Builder(101, new ComponentName("foo", "bar"));
- }
-
- private static JobStatus createJobStatus(JobInfo.Builder job) {
- return createJobStatus(job, android.os.Process.NOBODY_UID, 0, Long.MAX_VALUE);
- }
-
- private static JobStatus createJobStatus(JobInfo.Builder job, int uid) {
- return createJobStatus(job, uid, 0, Long.MAX_VALUE);
- }
-
- private static JobStatus createJobStatus(JobInfo.Builder job,
- long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
- return createJobStatus(job, android.os.Process.NOBODY_UID,
- earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
- }
-
- private static JobStatus createJobStatus(JobInfo.Builder job, int uid,
- long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
- return new JobStatus(job.build(), uid, null, -1, 0, 0, null,
- earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
index 77f6a23..acf511e 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
@@ -20,7 +20,6 @@
import static org.mockito.Mockito.mock;
import android.content.Context;
-import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerSaveState;
@@ -175,7 +174,7 @@
@SmallTest
public void testGetBatterySaverPolicy_PolicyQuickDoze_DefaultValueCorrect() {
- testServiceDefaultValue_Off(ServiceType.QUICK_DOZE);
+ testServiceDefaultValue_On(ServiceType.QUICK_DOZE);
}
@SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java b/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
index 70fadd1..00a62a3 100644
--- a/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
@@ -66,7 +66,7 @@
@Test
public void testParsePerSdkConfigSdkMinMax() throws JSONException, InvalidConfigException {
- JSONObject json = new JSONObject("{\"minSdk\":2, \"maxSdk\": 3, \"values\": []}");
+ JSONObject json = new JSONObject("{\"min_sdk\":2, \"max_sdk\": 3, \"values\": {}}");
SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, emptySet(),
emptyMap());
assertThat(config.minSdk).isEqualTo(2);
@@ -75,7 +75,7 @@
@Test
public void testParsePerSdkConfigNoMinSdk() throws JSONException {
- JSONObject json = new JSONObject("{\"maxSdk\": 3, \"values\": []}");
+ JSONObject json = new JSONObject("{\"max_sdk\": 3, \"values\": {}}");
try {
SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
@@ -86,7 +86,7 @@
@Test
public void testParsePerSdkConfigNoMaxSdk() throws JSONException {
- JSONObject json = new JSONObject("{\"minSdk\": 1, \"values\": []}");
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"values\": {}}");
try {
SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
@@ -97,7 +97,7 @@
@Test
public void testParsePerSdkConfigNoValues() throws JSONException {
- JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3}");
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"max_sdk\": 3}");
try {
SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
@@ -108,7 +108,7 @@
@Test
public void testParsePerSdkConfigSdkNullMinSdk() throws JSONException {
- JSONObject json = new JSONObject("{\"minSdk\":null, \"maxSdk\": 3, \"values\": []}");
+ JSONObject json = new JSONObject("{\"min_sdk\":null, \"max_sdk\": 3, \"values\": {}}");
try {
SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
@@ -119,7 +119,7 @@
@Test
public void testParsePerSdkConfigSdkNullMaxSdk() throws JSONException {
- JSONObject json = new JSONObject("{\"minSdk\":1, \"maxSdk\": null, \"values\": []}");
+ JSONObject json = new JSONObject("{\"min_sdk\":1, \"max_sdk\": null, \"values\": {}}");
try {
SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
@@ -130,7 +130,7 @@
@Test
public void testParsePerSdkConfigNullValues() throws JSONException {
- JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3, \"values\": null}");
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"max_sdk\": 3, \"values\": null}");
try {
SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
@@ -142,7 +142,7 @@
@Test
public void testParsePerSdkConfigZeroValues()
throws JSONException, InvalidConfigException {
- JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3, \"values\": []}");
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"max_sdk\": 3, \"values\": {}}");
SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
emptyMap());
assertThat(config.values).hasSize(0);
@@ -152,7 +152,7 @@
public void testParsePerSdkConfigSingleKey()
throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject(
- "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\"}}");
SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
emptyMap());
assertThat(config.values).containsExactly("a", "1");
@@ -162,7 +162,7 @@
public void testParsePerSdkConfigSingleKeyNullValue()
throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject(
- "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": null}]}");
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": null}}");
SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
emptyMap());
assertThat(config.values.keySet()).containsExactly("a");
@@ -173,8 +173,7 @@
public void testParsePerSdkConfigMultiKeys()
throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject(
- "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}, "
- + "{\"key\":\"c\", \"value\": \"2\"}]}");
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\", \"c\": \"2\"}}");
SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(
json, setOf("a", "b", "c"), emptyMap());
assertThat(config.values).containsExactly("a", "1", "c", "2");
@@ -183,7 +182,7 @@
@Test
public void testParsePerSdkConfigSingleKeyNotAllowed() throws JSONException {
JSONObject json = new JSONObject(
- "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\"}}");
try {
SignedConfig.parsePerSdkConfig(json, setOf("b"), emptyMap());
fail("Expected InvalidConfigException or JSONException");
@@ -196,7 +195,7 @@
public void testParsePerSdkConfigSingleKeyWithMap()
throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject(
- "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\"}}");
SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a"),
mapOf("a", mapOf("1", "one")));
assertThat(config.values).containsExactly("a", "one");
@@ -205,7 +204,7 @@
@Test
public void testParsePerSdkConfigSingleKeyWithMapInvalidValue() throws JSONException {
JSONObject json = new JSONObject(
- "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"2\"}]}");
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"2\"}}");
try {
SignedConfig.parsePerSdkConfig(json, setOf("b"), mapOf("a", mapOf("1", "one")));
fail("Expected InvalidConfigException");
@@ -218,8 +217,7 @@
public void testParsePerSdkConfigMultiKeysWithMap()
throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject(
- "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"},"
- + "{\"key\":\"b\", \"value\": \"1\"}]}");
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\", \"b\": \"1\"}}");
SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
mapOf("a", mapOf("1", "one")));
assertThat(config.values).containsExactly("a", "one", "b", "1");
@@ -229,7 +227,7 @@
public void testParsePerSdkConfigSingleKeyWithMapToNull()
throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject(
- "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": \"1\"}}");
SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a"),
mapOf("a", mapOf("1", null)));
assertThat(config.values).containsExactly("a", null);
@@ -241,25 +239,15 @@
assertThat(mapOf(null, "allitnil")).containsExactly(null, "allitnil");
assertThat(mapOf(null, "allitnil").containsKey(null)).isTrue();
JSONObject json = new JSONObject(
- "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": null}]}");
+ "{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {\"a\": null}}");
SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a"),
mapOf("a", mapOf(null, "allitnil")));
assertThat(config.values).containsExactly("a", "allitnil");
}
@Test
- public void testParsePerSdkConfigSingleKeyNoValue()
- throws JSONException, InvalidConfigException {
- JSONObject json = new JSONObject(
- "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\"}]}");
- SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
- emptyMap());
- assertThat(config.values).containsExactly("a", null);
- }
-
- @Test
public void testParsePerSdkConfigValuesInvalid() throws JSONException {
- JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1, \"values\": \"foo\"}");
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": \"foo\"}");
try {
SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
@@ -270,18 +258,7 @@
@Test
public void testParsePerSdkConfigConfigEntryInvalid() throws JSONException {
- JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [1, 2]}");
- try {
- SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
- fail("Expected InvalidConfigException or JSONException");
- } catch (JSONException | InvalidConfigException e) {
- // expected
- }
- }
-
- @Test
- public void testParsePerSdkConfigConfigEntryNull() throws JSONException {
- JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [null]}");
+ JSONObject json = new JSONObject("{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": [1, 2]}");
try {
SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
@@ -361,7 +338,7 @@
@Test
public void testParseSdkConfigSingle() throws InvalidConfigException {
SignedConfig config = SignedConfig.parse(
- "{\"version\": 1, \"config\":[{\"minSdk\": 1, \"maxSdk\": 1, \"values\": []}]}",
+ "{\"version\": 1, \"config\":[{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {}}]}",
emptySet(), emptyMap());
assertThat(config.perSdkConfig).hasSize(1);
}
@@ -369,8 +346,8 @@
@Test
public void testParseSdkConfigMultiple() throws InvalidConfigException {
SignedConfig config = SignedConfig.parse(
- "{\"version\": 1, \"config\":[{\"minSdk\": 1, \"maxSdk\": 1, \"values\": []}, "
- + "{\"minSdk\": 2, \"maxSdk\": 2, \"values\": []}]}", emptySet(),
+ "{\"version\": 1, \"config\":[{\"min_sdk\": 1, \"max_sdk\": 1, \"values\": {}}, "
+ + "{\"min_sdk\": 2, \"max_sdk\": 2, \"values\": {}}]}", emptySet(),
emptyMap());
assertThat(config.perSdkConfig).hasSize(2);
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index bf7f53d..860656b 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -16,6 +16,8 @@
package com.android.server.usage;
+import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE;
+
import static junit.framework.TestCase.fail;
import static org.testng.Assert.assertEquals;
@@ -136,9 +138,11 @@
event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP;
}
- event.mClass = ".fake.class.name" + i % 11;
+ final int instanceId = i % 11;
+ event.mClass = ".fake.class.name" + instanceId;
event.mTimeStamp = time;
- event.mEventType = i % 19; //"random" event type
+ event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type
+ event.mInstanceId = instanceId;
switch (event.mEventType) {
case Event.CONFIGURATION_CHANGE:
@@ -160,7 +164,8 @@
}
mIntervalStats.events.insert(event);
- mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType);
+ mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType,
+ event.mInstanceId);
time += timeProgression; // Arbitrary progression of time
}
@@ -219,7 +224,9 @@
// mBeginTimeStamp is based on the enclosing IntervalStats, don't bother checking
// mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking
assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
+ assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible);
assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
+ assertEquals(us1.mTotalTimeVisible, us2.mTotalTimeVisible);
assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed);
// mLaunchCount not persisted, so skipped
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 9556111..527a1ee 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MAX;
@@ -183,7 +184,7 @@
Resources mResources;
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
- TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
@Mock
private NotificationListeners mListeners;
@Mock private NotificationAssistants mAssistants;
@@ -430,7 +431,7 @@
@Test
public void testCreateNotificationChannels_SingleChannel() throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
final NotificationChannel createdChannel =
@@ -452,9 +453,9 @@
@Test
public void testCreateNotificationChannels_TwoChannels() throws Exception {
final NotificationChannel channel1 =
- new NotificationChannel("id1", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id1", "name", IMPORTANCE_DEFAULT);
final NotificationChannel channel2 =
- new NotificationChannel("id2", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id2", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel1, channel2)));
assertTrue(mBinderService.getNotificationChannel(PKG, "id1") != null);
@@ -465,7 +466,7 @@
public void testCreateNotificationChannels_SecondCreateDoesNotChangeImportance()
throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
@@ -476,14 +477,14 @@
new ParceledListSlice(Arrays.asList(dupeChannel)));
final NotificationChannel createdChannel =
mBinderService.getNotificationChannel(PKG, "id");
- assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
+ assertEquals(IMPORTANCE_DEFAULT, createdChannel.getImportance());
}
@Test
public void testCreateNotificationChannels_SecondCreateAllowedToDowngradeImportance()
throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
@@ -501,7 +502,7 @@
public void testCreateNotificationChannels_CannotDowngradeImportanceIfAlreadyUpdated()
throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
@@ -524,14 +525,14 @@
public void testCreateNotificationChannels_IdenticalChannelsInListIgnoresSecond()
throws Exception {
final NotificationChannel channel1 =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
final NotificationChannel channel2 =
new NotificationChannel("id", "name", IMPORTANCE_HIGH);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel1, channel2)));
final NotificationChannel createdChannel =
mBinderService.getNotificationChannel(PKG, "id");
- assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
+ assertEquals(IMPORTANCE_DEFAULT, createdChannel.getImportance());
}
@Test
@@ -756,13 +757,18 @@
@Test
public void testBlockedNotifications_blockedByAssistant() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_HIGH);
NotificationRecord r = generateNotificationRecord(channel);
mService.addEnqueuedNotification(r);
- r.setAssistantImportance(IMPORTANCE_NONE);
+ Bundle bundle = new Bundle();
+ bundle.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_NONE);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), bundle, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
NotificationManagerService.PostNotificationRunnable runnable =
mService.new PostNotificationRunnable(r.getKey());
@@ -1814,7 +1820,7 @@
mListener = mock(ManagedServices.ManagedServiceInfo.class);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
-try {
+ try {
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
fail("listeners that don't have a companion device shouldn't be able to call this");
@@ -2617,7 +2623,7 @@
}
@Test
- public void testAssistantIBlockingTriggersCancel() throws Exception {
+ public void testAssistantBlockingTriggersCancel() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
NotificationManagerService.WorkerHandler handler = mock(
@@ -2657,6 +2663,43 @@
}
@Test
+ public void testApplyEnqueuedAdjustmentFromAssistant_importance_onTime() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addEnqueuedNotification(r);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+ assertEquals(IMPORTANCE_LOW, r.getImportance());
+ }
+
+ @Test
+ public void testApplyEnqueuedAdjustmentFromAssistant_importance_tooLate() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+ assertEquals(IMPORTANCE_DEFAULT, r.getImportance());
+ assertFalse(r.hasAdjustment(Adjustment.KEY_IMPORTANCE));
+ }
+
+ @Test
public void testApplyEnqueuedAdjustmentFromAssistant_crossUser() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addEnqueuedNotification(r);
@@ -2856,7 +2899,7 @@
@Test
public void updateUriPermissions_update() throws Exception {
NotificationChannel c = new NotificationChannel(
- TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
Message message1 = new Message("", 0, "");
message1.setData("",
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 8690110..0eeeeed 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -760,6 +760,7 @@
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
record.setAssistantImportance(IMPORTANCE_LOW);
+ record.calculateImportance();
assertEquals(IMPORTANCE_LOW, record.getImportance());
// assistant ignored if user expressed preference
@@ -767,6 +768,7 @@
channel.lockFields(USER_LOCKED_IMPORTANCE);
record.setAssistantImportance(IMPORTANCE_LOW);
+ record.calculateImportance();
assertEquals(channel.getImportance(), record.getImportance());
}
@@ -779,6 +781,7 @@
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
record.setAssistantImportance(IMPORTANCE_LOW);
+ record.calculateImportance();
assertEquals(IMPORTANCE_LOW, record.getImportance());
record.updateNotificationChannel(
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 f6871b3..85410f5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -82,17 +82,17 @@
@Test
public void testEmptyTaskCleanupOnRemove() {
- assertNotNull(mTask.getWindowContainerController());
+ assertNotNull(mTask.getTask());
mStack.removeTask(mTask, "testEmptyTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
- assertNull(mTask.getWindowContainerController());
+ assertNull(mTask.getTask());
}
@Test
public void testOccupiedTaskCleanupOnRemove() {
final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
- assertNotNull(mTask.getWindowContainerController());
+ assertNotNull(mTask.getTask());
mStack.removeTask(mTask, "testOccupiedTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
- assertNotNull(mTask.getWindowContainerController());
+ assertNotNull(mTask.getTask());
}
@Test
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 f553c35..569c6d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -345,7 +345,7 @@
mStack.moveToFront("test");
mStack.addTask(task, true, "creating test task");
task.setStack(mStack);
- task.setWindowContainerController();
+ task.setTask();
}
task.touchActiveTime();
@@ -361,12 +361,12 @@
}
@Override
- void createWindowContainer(boolean onTop, boolean showForAllUsers) {
- setWindowContainerController();
+ void createTask(boolean onTop, boolean showForAllUsers) {
+ setTask();
}
- private void setWindowContainerController() {
- setWindowContainerController(mock(TaskWindowContainerController.class));
+ private void setTask() {
+ setTask(mock(Task.class));
}
}
}
@@ -447,7 +447,11 @@
}
@Override
- void updateUsageStats(ActivityRecord component, boolean resumed) {
+ void updateBatteryStats(ActivityRecord component, boolean resumed) {
+ }
+
+ @Override
+ void updateActivityUsageStats(ActivityRecord activity, int event) {
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
index ce5b13c..5690b58 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
@@ -41,17 +41,14 @@
public void testRemoveContainer() {
final StackWindowController stackController =
createStackControllerOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(stackController);
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController);
final TaskStack stack = stackController.mContainer;
- final Task task = taskController.mContainer;
assertNotNull(stack);
assertNotNull(task);
stackController.removeContainer();
// Assert that the container was removed.
assertNull(stackController.mContainer);
- assertNull(taskController.mContainer);
assertNull(stack.getDisplayContent());
assertNull(task.getDisplayContent());
assertNull(task.mStack);
@@ -61,11 +58,9 @@
public void testRemoveContainer_deferRemoval() {
final StackWindowController stackController =
createStackControllerOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(stackController);
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController);
final TaskStack stack = stackController.mContainer;
- final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer;
// Stack removal is deferred if one of its child is animating.
task.setLocalIsAnimating(true);
@@ -75,11 +70,12 @@
// the stack window container is removed.
assertNull(stackController.mContainer);
assertNull(stack.getController());
- assertNotNull(taskController.mContainer);
- assertNotNull(task.getController());
+ assertNotNull(task);
stack.removeImmediately();
- assertNull(taskController.mContainer);
+ // After removing, the task will be isolated.
+ assertNull(task.getParent());
+ assertEquals(task.getChildCount(), 0);
assertNull(task.getController());
}
@@ -89,9 +85,7 @@
final StackWindowController stack1Controller =
createStackControllerOnDisplay(mDisplayContent);
final TaskStack stack1 = stack1Controller.mContainer;
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(stack1Controller);
- final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer;
+ final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1Controller);
task1.mOnDisplayChangedCalled = false;
// Create second display and put second stack on it.
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 630a8bf..6b6b33c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+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.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
@@ -31,10 +34,12 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
import android.util.Xml;
+import android.view.DisplayInfo;
import androidx.test.filters.MediumTest;
@@ -66,10 +71,13 @@
private static final String TASK_TAG = "task";
+ private Rect mParentBounds;
+
@Before
public void setUp() throws Exception {
TaskRecord.setTaskRecordFactory(null);
setupActivityTaskManagerService();
+ mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
}
@Test
@@ -124,6 +132,72 @@
assertTrue(task.returnsToHomeStack());
}
+ /** Ensures that bounds are clipped to their parent. */
+ @Test
+ public void testAppBounds_BoundsClipping() {
+ final Rect shiftedBounds = new Rect(mParentBounds);
+ shiftedBounds.offset(10, 10);
+ final Rect expectedBounds = new Rect(mParentBounds);
+ expectedBounds.intersect(shiftedBounds);
+ testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds,
+ expectedBounds);
+ }
+
+ /** Ensures that empty bounds are not propagated to the configuration. */
+ @Test
+ public void testAppBounds_EmptyBounds() {
+ final Rect emptyBounds = new Rect();
+ testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
+ null /*ExpectedBounds*/);
+ }
+
+ /** Ensures that bounds on freeform stacks are not clipped. */
+ @Test
+ public void testAppBounds_FreeFormBounds() {
+ final Rect freeFormBounds = new Rect(mParentBounds);
+ freeFormBounds.offset(10, 10);
+ testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
+ freeFormBounds);
+ }
+
+ /** Ensures that fully contained bounds are not clipped. */
+ @Test
+ public void testAppBounds_ContainedBounds() {
+ final Rect insetBounds = new Rect(mParentBounds);
+ insetBounds.inset(5, 5, 5, 5);
+ testStackBoundsConfiguration(
+ WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds);
+ }
+
+ /** Ensures that full screen free form bounds are clipped */
+ @Test
+ public void testAppBounds_FullScreenFreeFormBounds() {
+ ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+ DisplayInfo info = new DisplayInfo();
+ display.mDisplay.getDisplayInfo(info);
+ final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
+ testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
+ mParentBounds);
+ }
+
+ private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
+ Rect expectedConfigBounds) {
+
+ ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+ ActivityStack stack = display.createStack(windowingMode, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+
+ final Configuration parentConfig = stack.getConfiguration();
+ parentConfig.windowConfiguration.setAppBounds(parentBounds);
+ task.setBounds(bounds);
+
+ task.resolveOverrideConfiguration(parentConfig);
+ // Assert that both expected and actual are null or are equal to each other
+ assertEquals(expectedConfigBounds,
+ task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds());
+ }
+
private byte[] serializeToBytes(TaskRecord r) throws IOException, XmlPullParserException {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
final XmlSerializer serializer = Xml.newSerializer();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
new file mode 100644
index 0000000..3e32ed3
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -0,0 +1,136 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Test class for {@link Task}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:TaskTests
+ */
+@SmallTest
+@Presubmit
+public class TaskTests extends WindowTestsBase {
+
+ @Test
+ public void testRemoveContainer() {
+ final StackWindowController stackController1 =
+ createStackControllerOnDisplay(mDisplayContent);
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
+ final WindowTestUtils.TestAppWindowToken appToken =
+ WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task);
+
+ task.removeIfPossible();
+ // Assert that the container was removed.
+ assertNull(task.getParent());
+ assertEquals(task.getChildCount(), 0);
+ assertNull(appToken.getParent());
+ }
+
+ @Test
+ public void testRemoveContainer_deferRemoval() {
+ final StackWindowController stackController1 =
+ createStackControllerOnDisplay(mDisplayContent);
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
+ final WindowTestUtils.TestAppWindowToken appToken =
+ WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task);
+
+ task.mShouldDeferRemoval = true;
+
+ task.removeIfPossible();
+ // For the case of deferred removal the task will still be connected to the its app token
+ // until the task window container is removed.
+ assertNotNull(task.getParent());
+ assertNotEquals(task.getChildCount(), 0);
+ assertNotNull(appToken.getParent());
+
+ task.removeImmediately();
+ assertNull(task.getParent());
+ assertEquals(task.getChildCount(), 0);
+ assertNull(appToken.getParent());
+ }
+
+ @Test
+ public void testReparent() {
+ final StackWindowController stackController1 =
+ createStackControllerOnDisplay(mDisplayContent);
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
+ final StackWindowController stackController2 =
+ createStackControllerOnDisplay(mDisplayContent);
+ final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stackController2);
+
+ boolean gotException = false;
+ try {
+ task.reparent(stackController1, 0, false/* moveParents */);
+ } catch (IllegalArgumentException e) {
+ gotException = true;
+ }
+ assertTrue("Should not be able to reparent to the same parent", gotException);
+
+ final StackWindowController stackController3 =
+ createStackControllerOnDisplay(mDisplayContent);
+ stackController3.setContainer(null);
+ gotException = false;
+ try {
+ task.reparent(stackController3, 0, false/* moveParents */);
+ } catch (IllegalArgumentException e) {
+ gotException = true;
+ }
+ assertTrue("Should not be able to reparent to a stack that doesn't have a container",
+ gotException);
+
+ task.reparent(stackController2, 0, false/* moveParents */);
+ assertEquals(stackController2.mContainer, task.getParent());
+ assertEquals(0, task.positionInParent());
+ assertEquals(1, task2.positionInParent());
+ }
+
+ @Test
+ public void testReparent_BetweenDisplays() {
+ // Create first stack on primary display.
+ final StackWindowController stack1Controller =
+ createStackControllerOnDisplay(mDisplayContent);
+ final TaskStack stack1 = stack1Controller.mContainer;
+ final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1Controller);
+ task.mOnDisplayChangedCalled = false;
+ assertEquals(mDisplayContent, stack1.getDisplayContent());
+
+ // Create second display and put second stack on it.
+ final DisplayContent dc = createNewDisplay();
+ final StackWindowController stack2Controller = createStackControllerOnDisplay(dc);
+ final TaskStack stack2 = stack2Controller.mContainer;
+ final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2Controller);
+ // Reparent and check state
+ task.reparent(stack2Controller, 0, false /* moveParents */);
+ assertEquals(stack2, task.getParent());
+ assertEquals(0, task.positionInParent());
+ assertEquals(1, task2.positionInParent());
+ assertTrue(task.mOnDisplayChangedCalled);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
deleted file mode 100644
index bbf508d..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ /dev/null
@@ -1,145 +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 org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Test class for {@link TaskWindowContainerController}.
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:TaskWindowContainerControllerTests
- */
-@SmallTest
-@Presubmit
-public class TaskWindowContainerControllerTests extends WindowTestsBase {
-
- /* Comment out due to removal of AppWindowContainerController
- @Test
- public void testRemoveContainer() {
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(this);
- final WindowTestUtils.TestAppWindowContainerController appController =
- new WindowTestUtils.TestAppWindowContainerController(taskController);
-
- taskController.removeContainer();
- // Assert that the container was removed.
- assertNull(taskController.mContainer);
- assertNull(appController.mContainer);
- }
- */
-
- /* Comment out due to removal of AppWindowContainerController
- @Test
- public void testRemoveContainer_deferRemoval() {
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(this);
- final WindowTestUtils.TestAppWindowContainerController appController =
- new WindowTestUtils.TestAppWindowContainerController(taskController);
-
- final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer;
- final AppWindowToken app = appController.mContainer;
- task.mShouldDeferRemoval = true;
-
- taskController.removeContainer();
- // For the case of deferred removal the task controller will no longer be connected to the
- // container, but the app controller will still be connected to the its container until
- // the task window container is removed.
- assertNull(taskController.mContainer);
- assertNull(task.getController());
- assertNotNull(appController.mContainer);
- assertNotNull(app.getController());
-
- task.removeImmediately();
- assertNull(appController.mContainer);
- assertNull(app.getController());
- }
- */
-
- @Test
- public void testReparent() {
- final StackWindowController stackController1 =
- createStackControllerOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(stackController1);
- final StackWindowController stackController2 =
- createStackControllerOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTaskWindowContainerController taskController2 =
- new WindowTestUtils.TestTaskWindowContainerController(stackController2);
-
- boolean gotException = false;
- try {
- taskController.reparent(stackController1, 0, false/* moveParents */);
- } catch (IllegalArgumentException e) {
- gotException = true;
- }
- assertTrue("Should not be able to reparent to the same parent", gotException);
-
- final StackWindowController stackController3 =
- createStackControllerOnDisplay(mDisplayContent);
- stackController3.setContainer(null);
- gotException = false;
- try {
- taskController.reparent(stackController3, 0, false/* moveParents */);
- } catch (IllegalArgumentException e) {
- gotException = true;
- }
- assertTrue("Should not be able to reparent to a stack that doesn't have a container",
- gotException);
-
- taskController.reparent(stackController2, 0, false/* moveParents */);
- assertEquals(stackController2.mContainer, taskController.mContainer.getParent());
- assertEquals(0, ((WindowTestUtils.TestTask) taskController.mContainer).positionInParent());
- assertEquals(1, ((WindowTestUtils.TestTask) taskController2.mContainer).positionInParent());
- }
-
- @Test
- public void testReparent_BetweenDisplays() {
- // Create first stack on primary display.
- final StackWindowController stack1Controller =
- createStackControllerOnDisplay(mDisplayContent);
- final TaskStack stack1 = stack1Controller.mContainer;
- final WindowTestUtils.TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(stack1Controller);
- final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer;
- task1.mOnDisplayChangedCalled = false;
- assertEquals(mDisplayContent, stack1.getDisplayContent());
-
- // Create second display and put second stack on it.
- final DisplayContent dc = createNewDisplay();
- final StackWindowController stack2Controller = createStackControllerOnDisplay(dc);
- final TaskStack stack2 = stack2Controller.mContainer;
- final WindowTestUtils.TestTaskWindowContainerController taskController2 =
- new WindowTestUtils.TestTaskWindowContainerController(stack2Controller);
- final WindowTestUtils.TestTask task2 =
- (WindowTestUtils.TestTask) taskController2.mContainer;
-
- // Reparent and check state
- taskController.reparent(stack2Controller, 0, false /* moveParents */);
- assertEquals(stack2, task1.getParent());
- assertEquals(0, task1.positionInParent());
- assertEquals(1, task2.positionInParent());
- assertTrue(task1.mOnDisplayChangedCalled);
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index 885a7e0..c653762f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -19,7 +19,6 @@
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.WINDOW_CONFIG_ALWAYS_ON_TOP;
import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
import static android.app.WindowConfiguration.WINDOW_CONFIG_ROTATION;
@@ -174,70 +173,4 @@
assertEquals(appBounds.width(), info.appWidth);
assertEquals(appBounds.height(), info.appHeight);
}
-
- /** Ensures that bounds are clipped to their parent. */
- @Test
- public void testAppBounds_BoundsClipping() {
- final Rect shiftedBounds = new Rect(mParentBounds);
- shiftedBounds.offset(10, 10);
- final Rect expectedBounds = new Rect(mParentBounds);
- expectedBounds.intersect(shiftedBounds);
- testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds,
- expectedBounds);
- }
-
- /** Ensures that empty bounds are not propagated to the configuration. */
- @Test
- public void testAppBounds_EmptyBounds() {
- final Rect emptyBounds = new Rect();
- testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
- null /*ExpectedBounds*/);
- }
-
- /** Ensures that bounds on freeform stacks are not clipped. */
- @Test
- public void testAppBounds_FreeFormBounds() {
- final Rect freeFormBounds = new Rect(mParentBounds);
- freeFormBounds.offset(10, 10);
- testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
- freeFormBounds);
- }
-
- /** Ensures that fully contained bounds are not clipped. */
- @Test
- public void testAppBounds_ContainedBounds() {
- final Rect insetBounds = new Rect(mParentBounds);
- insetBounds.inset(5, 5, 5, 5);
- testStackBoundsConfiguration(
- WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds);
- }
-
- /** Ensures that full screen free form bounds are clipped */
- @Test
- public void testAppBounds_FullScreenFreeFormBounds() {
- final Rect fullScreenBounds = new Rect(0, 0, mDisplayInfo.logicalWidth,
- mDisplayInfo.logicalHeight);
- testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
- mParentBounds);
- }
-
- private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
- Rect expectedConfigBounds) {
- final StackWindowController stackController = createStackControllerOnStackOnDisplay(
- windowingMode, ACTIVITY_TYPE_STANDARD, mDisplayContent);
-
- final Configuration parentConfig = mDisplayContent.getConfiguration();
- parentConfig.windowConfiguration.setAppBounds(parentBounds);
-
- final Configuration config = new Configuration();
- final WindowConfiguration winConfig = config.windowConfiguration;
- stackController.adjustConfigurationForBounds(bounds,
- new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/,
- false /*overrideHeight*/, mDisplayInfo.logicalDensityDpi, config, parentConfig,
- windowingMode);
- // Assert that both expected and actual are null or are equal to each other
-
- assertEquals(expectedConfigBounds, winConfig.getAppBounds());
- }
-
}
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 65e1835..65cde77 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -19,11 +19,6 @@
import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -34,8 +29,6 @@
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;
@@ -45,8 +38,6 @@
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
-import org.mockito.invocation.InvocationOnMock;
-
/**
* A collection of static functions that can be referenced by other test packages to provide access
* to WindowManager related test functionality.
@@ -116,17 +107,6 @@
public static StackWindowController createMockStackWindowContainerController() {
StackWindowController controller = mock(StackWindowController.class);
controller.mContainer = mock(TestTaskStack.class);
-
- // many components rely on the {@link StackWindowController#adjustConfigurationForBounds}
- // to properly set bounds values in the configuration. We must mimick those actions here.
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final Configuration config = invocationOnMock.<Configuration>getArgument(6);
- final Rect bounds = invocationOnMock.<Rect>getArgument(0);
- config.windowConfiguration.setBounds(bounds);
- return null;
- }).when(controller).adjustConfigurationForBounds(any(), any(), any(),
- anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
-
return controller;
}
@@ -141,6 +121,13 @@
}
}
+ /** Creates an {@link AppWindowToken} and adds it to the specified {@link Task}. */
+ public static TestAppWindowToken createAppWindowTokenInTask(DisplayContent dc, Task task) {
+ final TestAppWindowToken newToken = createTestAppWindowToken(dc);
+ task.addChild(newToken, POSITION_TOP);
+ return newToken;
+ }
+
/**
* An extension of {@link TestTaskStack}, which overrides package scoped methods that would not
* normally be mocked out.
@@ -264,9 +251,10 @@
TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
int resizeMode, boolean supportsPictureInPicture,
- TaskWindowContainerController controller) {
+ TaskRecord taskRecord) {
super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
- new ActivityManager.TaskDescription(), controller);
+ new ActivityManager.TaskDescription(), taskRecord);
+ stack.addTask(this, POSITION_TOP);
}
boolean shouldDeferRemoval() {
@@ -293,49 +281,10 @@
}
}
- /**
- * Used so we can gain access to some protected members of {@link TaskWindowContainerController}
- * class.
- */
- public static class TestTaskWindowContainerController extends TaskWindowContainerController {
-
- static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() {
- @Override
- public void registerConfigurationChangeListener(
- ConfigurationContainerListener listener) {
- }
-
- @Override
- public void unregisterConfigurationChangeListener(
- ConfigurationContainerListener listener) {
- }
-
- @Override
- public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
- }
-
- @Override
- public void requestResize(Rect bounds, int resizeMode) {
- }
- };
-
- TestTaskWindowContainerController(WindowTestsBase testsBase) {
- this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent));
- }
-
- TestTaskWindowContainerController(StackWindowController stackController) {
- super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */,
- RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/,
- true /* showForAllUsers */, new ActivityManager.TaskDescription(),
- stackController.mService);
- }
-
- @Override
- TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode,
- boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) {
- return new TestTask(taskId, stack, userId, mService, resizeMode,
- supportsPictureInPicture, this);
- }
+ public static TestTask createTestTask(StackWindowController stackWindowController) {
+ return new TestTask(sNextTaskId++, stackWindowController.mContainer, 0,
+ stackWindowController.mService, RESIZE_MODE_UNRESIZEABLE, false,
+ mock(TaskRecord.class));
}
public static class TestIApplicationToken implements IApplicationToken {
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 4f573a4..65ed85d 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -842,8 +842,8 @@
final boolean previouslyIdle = mAppIdleHistory.isIdle(
event.mPackage, userId, elapsedRealtime);
// Inform listeners if necessary
- if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
- || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
+ if ((event.mEventType == UsageEvents.Event.ACTIVITY_RESUMED
+ || event.mEventType == UsageEvents.Event.ACTIVITY_PAUSED
|| event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
|| event.mEventType == UsageEvents.Event.USER_INTERACTION
|| event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
@@ -894,8 +894,8 @@
private int usageEventToSubReason(int eventType) {
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.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+ case UsageEvents.Event.ACTIVITY_PAUSED: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
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/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 8405267..94cc650 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -15,10 +15,31 @@
*/
package com.android.server.usage;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
+import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
+import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
+import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.END_OF_DAY;
+import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
+import static android.app.usage.UsageEvents.Event.KEYGUARD_HIDDEN;
+import static android.app.usage.UsageEvents.Event.KEYGUARD_SHOWN;
+import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.SCREEN_INTERACTIVE;
+import static android.app.usage.UsageEvents.Event.SCREEN_NON_INTERACTIVE;
+import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+import static android.app.usage.UsageEvents.Event.STANDBY_BUCKET_CHANGED;
+import static android.app.usage.UsageEvents.Event.SYSTEM_INTERACTION;
+
import android.app.usage.ConfigurationStats;
import android.app.usage.EventList;
import android.app.usage.EventStats;
-import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
import android.util.ArrayMap;
@@ -125,8 +146,8 @@
/**
* Builds a UsageEvents.Event, but does not add it internally.
*/
- UsageEvents.Event buildEvent(String packageName, String className) {
- UsageEvents.Event event = new UsageEvents.Event();
+ Event buildEvent(String packageName, String className) {
+ Event event = new Event();
event.mPackage = getCachedStringRef(packageName);
if (className != null) {
event.mClass = getCachedStringRef(className);
@@ -138,9 +159,9 @@
* Builds a UsageEvents.Event from a proto, but does not add it internally.
* Built here to take advantage of the cached String Refs
*/
- UsageEvents.Event buildEvent(ProtoInputStream parser, List<String> stringPool)
+ Event buildEvent(ProtoInputStream parser, List<String> stringPool)
throws IOException {
- final UsageEvents.Event event = new UsageEvents.Event();
+ final Event event = new Event();
while (true) {
switch (parser.nextField()) {
case (int) IntervalStatsProto.Event.PACKAGE:
@@ -190,20 +211,23 @@
parser.readInt(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX)
- 1));
break;
+ case (int) IntervalStatsProto.Event.INSTANCE_ID:
+ event.mInstanceId = parser.readInt(IntervalStatsProto.Event.INSTANCE_ID);
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
// Handle default values for certain events types
switch (event.mEventType) {
- case UsageEvents.Event.CONFIGURATION_CHANGE:
+ case CONFIGURATION_CHANGE:
if (event.mConfiguration == null) {
event.mConfiguration = new Configuration();
}
break;
- case UsageEvents.Event.SHORTCUT_INVOCATION:
+ case SHORTCUT_INVOCATION:
if (event.mShortcutId == null) {
event.mShortcutId = "";
}
break;
- case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ case NOTIFICATION_INTERRUPTION:
if (event.mNotificationChannelId == null) {
event.mNotificationChannelId = "";
}
@@ -220,14 +244,15 @@
private boolean isStatefulEvent(int eventType) {
switch (eventType) {
- case UsageEvents.Event.MOVE_TO_FOREGROUND:
- case UsageEvents.Event.MOVE_TO_BACKGROUND:
- case UsageEvents.Event.FOREGROUND_SERVICE_START:
- case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
- case UsageEvents.Event.END_OF_DAY:
- case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE:
- case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
- case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE:
+ case ACTIVITY_RESUMED:
+ case ACTIVITY_PAUSED:
+ case ACTIVITY_STOPPED:
+ case FOREGROUND_SERVICE_START:
+ case FOREGROUND_SERVICE_STOP:
+ case END_OF_DAY:
+ case ROLLOVER_FOREGROUND_SERVICE:
+ case CONTINUE_PREVIOUS_DAY:
+ case CONTINUING_FOREGROUND_SERVICE:
return true;
}
return false;
@@ -238,17 +263,56 @@
* interaction. Excludes those that are internally generated.
*/
private boolean isUserVisibleEvent(int eventType) {
- return eventType != UsageEvents.Event.SYSTEM_INTERACTION
- && eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED;
+ return eventType != SYSTEM_INTERACTION
+ && eventType != STANDBY_BUCKET_CHANGED;
}
/**
+ * Update the IntervalStats by a activity or foreground service event.
+ * @param packageName package name of this event. Is null if event targets to all packages.
+ * @param className class name of a activity or foreground service, could be null to if this
+ * is sent to all activities/services in this package.
+ * @param timeStamp Epoch timestamp in milliseconds.
+ * @param eventType event type as in {@link Event}
+ * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken.
+ * if className is not an activity, instanceId is not used.
* @hide
*/
@VisibleForTesting
- public void update(String packageName, String className, long timeStamp, int eventType) {
- UsageStats usageStats = getOrCreateUsageStats(packageName);
- usageStats.update(className, timeStamp, eventType);
+ public void update(String packageName, String className, long timeStamp, int eventType,
+ int instanceId) {
+ if (eventType == FLUSH_TO_DISK) {
+ // FLUSH_TO_DISK are sent to all packages.
+ final int size = packageStats.size();
+ for (int i = 0; i < size; i++) {
+ UsageStats usageStats = packageStats.valueAt(i);
+ usageStats.update(null, timeStamp, eventType, instanceId);
+ }
+ } else if (eventType == ACTIVITY_DESTROYED) {
+ UsageStats usageStats = packageStats.get(packageName);
+ if (usageStats != null) {
+ // If previous event is not ACTIVITY_STOPPED, convert ACTIVITY_DESTROYED
+ // to ACTIVITY_STOPPED and add to event list.
+ // Otherwise do not add anything to event list. (Because we want to save space
+ // and we do not want a ACTIVITY_STOPPED followed by
+ // ACTIVITY_DESTROYED in event list).
+ final int index = usageStats.mActivities.indexOfKey(instanceId);
+ if (index >= 0) {
+ final int type = usageStats.mActivities.valueAt(index);
+ if (type != ACTIVITY_STOPPED) {
+ Event event = new Event(ACTIVITY_STOPPED, timeStamp);
+ event.mPackage = packageName;
+ event.mClass = className;
+ event.mInstanceId = instanceId;
+ addEvent(event);
+ }
+ }
+ usageStats.update(className, timeStamp, ACTIVITY_DESTROYED, instanceId);
+ }
+ } else {
+ UsageStats usageStats = getOrCreateUsageStats(packageName);
+ usageStats.update(className, timeStamp, eventType, instanceId);
+ }
endTime = timeStamp;
}
@@ -256,7 +320,7 @@
* @hide
*/
@VisibleForTesting
- public void addEvent(UsageEvents.Event event) {
+ public void addEvent(Event event) {
if (events == null) {
events = new EventList();
}
@@ -265,7 +329,7 @@
if (event.mClass != null) {
event.mClass = getCachedStringRef(event.mClass);
}
- if (event.mEventType == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ if (event.mEventType == NOTIFICATION_INTERRUPTION) {
event.mNotificationChannelId = getCachedStringRef(event.mNotificationChannelId);
}
events.insert(event);
@@ -338,13 +402,13 @@
}
void addEventStatsTo(List<EventStats> out) {
- interactiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_INTERACTIVE,
+ interactiveTracker.addToEventStats(out, SCREEN_INTERACTIVE,
beginTime, endTime);
- nonInteractiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_NON_INTERACTIVE,
+ nonInteractiveTracker.addToEventStats(out, SCREEN_NON_INTERACTIVE,
beginTime, endTime);
- keyguardShownTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_SHOWN,
+ keyguardShownTracker.addToEventStats(out, KEYGUARD_SHOWN,
beginTime, endTime);
- keyguardHiddenTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_HIDDEN,
+ keyguardHiddenTracker.addToEventStats(out, KEYGUARD_HIDDEN,
beginTime, endTime);
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index 8e1c060..d706537 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -135,6 +135,15 @@
stats.mTotalTimeForegroundServiceUsed = proto.readLong(
IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS);
break;
+ case (int) IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS:
+ // Time attributes stored is an offset of the beginTime.
+ stats.mLastTimeVisible = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS);
+ break;
+ case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS:
+ stats.mTotalTimeVisible = proto.readLong(
+ IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS);
+ break;
}
}
if (stats.mLastTimeUsed == 0) {
@@ -337,6 +346,11 @@
usageStats.mLastTimeForegroundServiceUsed - stats.beginTime);
proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS,
usageStats.mTotalTimeForegroundServiceUsed);
+ // Time attributes stored as an offset of the beginTime.
+ proto.write(IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS,
+ usageStats.mLastTimeVisible - stats.beginTime);
+ proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS,
+ usageStats.mTotalTimeVisible);
proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
writeChooserCounts(proto, usageStats);
proto.end(token);
@@ -427,6 +441,7 @@
proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime);
proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags);
proto.write(IntervalStatsProto.Event.TYPE, event.mEventType);
+ proto.write(IntervalStatsProto.Event.INSTANCE_ID, event.mInstanceId);
switch (event.mEventType) {
case UsageEvents.Event.CONFIGURATION_CHANGE:
if (event.mConfiguration != null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2621252..57dc08f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -16,6 +16,12 @@
package com.android.server.usage;
+import static android.app.usage.UsageEvents.Event.CHOOSER_ACTION;
+import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
+import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
+import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+
import android.Manifest;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -28,10 +34,10 @@
import android.app.usage.EventStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManager;
-import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -75,9 +81,7 @@
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
/**
* A service that collects, aggregates, and persists application usage data.
@@ -106,6 +110,7 @@
static final int MSG_FLUSH_TO_DISK = 1;
static final int MSG_REMOVE_USER = 2;
static final int MSG_UID_STATE_CHANGED = 3;
+ static final int MSG_REPORT_EVENT_TO_ALL_USERID = 4;
private final Object mLock = new Object();
Handler mHandler;
@@ -135,12 +140,10 @@
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
int bucket, int reason) {
- Event event = new Event();
- event.mEventType = Event.STANDBY_BUCKET_CHANGED;
+ Event event = new Event(Event.STANDBY_BUCKET_CHANGED,
+ SystemClock.elapsedRealtime());
event.mBucketAndReason = (bucket << 16) | (reason & 0xFFFF);
event.mPackage = packageName;
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -397,7 +400,7 @@
* Assuming the event's timestamp is measured in milliseconds since boot,
* convert it to a system wall time.
*/
- private void convertToSystemTimeLocked(UsageEvents.Event event) {
+ private void convertToSystemTimeLocked(Event event) {
event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
}
@@ -406,7 +409,6 @@
*/
void shutdown() {
synchronized (mLock) {
- mHandler.removeMessages(MSG_REPORT_EVENT);
flushToDiskLocked();
}
}
@@ -414,7 +416,7 @@
/**
* Called by the Binder stub.
*/
- void reportEvent(UsageEvents.Event event, int userId) {
+ void reportEvent(Event event, int userId) {
synchronized (mLock) {
final long timeNow = checkAndGetTimeLocked();
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -431,14 +433,14 @@
mAppStandby.reportEvent(event, elapsedRealtime, userId);
switch (event.mEventType) {
- case Event.MOVE_TO_FOREGROUND:
+ case Event.ACTIVITY_RESUMED:
try {
mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
} catch (IllegalArgumentException iae) {
Slog.e(TAG, "Failed to note usage start", iae);
}
break;
- case Event.MOVE_TO_BACKGROUND:
+ case Event.ACTIVITY_PAUSED:
try {
mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
} catch (IllegalArgumentException iae) {
@@ -450,10 +452,31 @@
}
/**
- * Called by the Binder stub.
+ * Some events like FLUSH_TO_DISK need to be sent to all userId.
+ * @param event
+ */
+ void reportEventToAllUserId(Event event) {
+ synchronized (mLock) {
+ final int userCount = mUserState.size();
+ for (int i = 0; i < userCount; i++) {
+ Event copy = new Event(event);
+ reportEvent(copy, mUserState.keyAt(i));
+ }
+ }
+ }
+
+ /**
+ * Called by the Handler for message MSG_FLUSH_TO_DISK.
*/
void flushToDisk() {
synchronized (mLock) {
+ // Before flush to disk, report FLUSH_TO_DISK event to signal UsageStats to update app
+ // usage. In case of abrupt power shutdown like battery drain or cold temperature,
+ // all UsageStats has correct data up to last flush to disk.
+ // The FLUSH_TO_DISK event is an internal event, it will not show up in IntervalStats'
+ // EventList.
+ Event event = new Event(FLUSH_TO_DISK, SystemClock.elapsedRealtime());
+ reportEventToAllUserId(event);
flushToDiskLocked();
}
}
@@ -656,9 +679,11 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REPORT_EVENT:
- reportEvent((UsageEvents.Event) msg.obj, msg.arg1);
+ reportEvent((Event) msg.obj, msg.arg1);
break;
-
+ case MSG_REPORT_EVENT_TO_ALL_USERID:
+ reportEventToAllUserId((Event) msg.obj);
+ break;
case MSG_FLUSH_TO_DISK:
flushToDisk();
break;
@@ -1120,20 +1145,11 @@
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(CHOOSER_ACTION, SystemClock.elapsedRealtime());
event.mPackage = packageName;
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = Event.CHOOSER_ACTION;
-
event.mAction = action;
-
event.mContentType = contentType;
-
event.mContentAnnotations = annotations;
-
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -1251,37 +1267,29 @@
private final class LocalService extends UsageStatsManagerInternal {
@Override
- public void reportEvent(ComponentName component, int userId, int eventType) {
+ public void reportEvent(ComponentName component, int userId, int eventType,
+ int instanceId) {
if (component == null) {
Slog.w(TAG, "Event reported without a component name");
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(eventType, SystemClock.elapsedRealtime());
event.mPackage = component.getPackageName();
event.mClass = component.getClassName();
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = eventType;
+ event.mInstanceId = instanceId;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@Override
public void reportEvent(String packageName, int userId, int eventType) {
if (packageName == null) {
- Slog.w(TAG, "Event reported without a package name");
+ Slog.w(TAG, "Event reported without a package name, eventType:" + eventType);
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(eventType, SystemClock.elapsedRealtime());
event.mPackage = packageName;
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = eventType;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -1292,13 +1300,8 @@
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(CONFIGURATION_CHANGE, SystemClock.elapsedRealtime());
event.mPackage = "android";
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
event.mConfiguration = new Configuration(config);
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -1311,14 +1314,9 @@
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(NOTIFICATION_INTERRUPTION, SystemClock.elapsedRealtime());
event.mPackage = packageName.intern();
event.mNotificationChannelId = channelId.intern();
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = Event.NOTIFICATION_INTERRUPTION;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -1329,14 +1327,9 @@
return;
}
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event(SHORTCUT_INVOCATION, SystemClock.elapsedRealtime());
event.mPackage = packageName.intern();
event.mShortcutId = shortcutId.intern();
-
- // This will later be converted to system time.
- event.mTimeStamp = SystemClock.elapsedRealtime();
-
- event.mEventType = Event.SHORTCUT_INVOCATION;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@@ -1372,7 +1365,7 @@
// This method *WILL* do IO work, but we must block until it is finished or else
// we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
// we are shutting down.
- shutdown();
+ UsageStatsService.this.shutdown();
}
@Override
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 01e566c..ec475bf 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -61,11 +61,13 @@
private static final String FLAGS_ATTR = "flags";
private static final String CLASS_ATTR = "class";
private static final String TOTAL_TIME_ACTIVE_ATTR = "timeActive";
+ private static final String TOTAL_TIME_VISIBLE_ATTR = "timeVisible";
private static final String TOTAL_TIME_SERVICE_USED_ATTR = "timeServiceUsed";
private static final String COUNT_ATTR = "count";
private static final String ACTIVE_ATTR = "active";
private static final String LAST_EVENT_ATTR = "lastEvent";
private static final String TYPE_ATTR = "type";
+ private static final String INSTANCE_ID_ATTR = "instanceId";
private static final String SHORTCUT_ID_ATTR = "shortcutId";
private static final String STANDBY_BUCKET_ATTR = "standbyBucket";
private static final String APP_LAUNCH_COUNT_ATTR = "appLaunchCount";
@@ -75,6 +77,7 @@
// Time attributes stored as an offset of the beginTime.
private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+ private static final String LAST_TIME_VISIBLE_ATTR = "lastTimeVisible";
private static final String LAST_TIME_SERVICE_USED_ATTR = "lastTimeServiceUsed";
private static final String END_TIME_ATTR = "endTime";
private static final String TIME_ATTR = "time";
@@ -92,6 +95,13 @@
parser, LAST_TIME_ACTIVE_ATTR);
try {
+ stats.mLastTimeVisible = statsOut.beginTime + XmlUtils.readLongAttribute(
+ parser, LAST_TIME_VISIBLE_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mLastTimeVisible", e);
+ }
+
+ try {
stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
parser, LAST_TIME_SERVICE_USED_ATTR);
} catch (IOException e) {
@@ -101,8 +111,14 @@
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
try {
+ stats.mTotalTimeVisible = XmlUtils.readLongAttribute(parser, TOTAL_TIME_VISIBLE_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mTotalTimeVisible", e);
+ }
+
+ try {
stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
- TOTAL_TIME_SERVICE_USED_ATTR);
+ TOTAL_TIME_SERVICE_USED_ATTR);
} catch (IOException e) {
Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
}
@@ -199,6 +215,13 @@
event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR);
event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
+
+ try {
+ event.mInstanceId = XmlUtils.readIntAttribute(parser, INSTANCE_ID_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mInstanceId", e);
+ }
+
switch (event.mEventType) {
case UsageEvents.Event.CONFIGURATION_CHANGE:
event.mConfiguration = new Configuration();
@@ -227,10 +250,13 @@
// Write the time offset.
XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
usageStats.mLastTimeUsed - stats.beginTime);
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_VISIBLE_ATTR,
+ usageStats.mLastTimeVisible - stats.beginTime);
XmlUtils.writeLongAttribute(xml, LAST_TIME_SERVICE_USED_ATTR,
usageStats.mLastTimeForegroundServiceUsed - stats.beginTime);
XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_VISIBLE_ATTR, usageStats.mTotalTimeVisible);
XmlUtils.writeLongAttribute(xml, TOTAL_TIME_SERVICE_USED_ATTR,
usageStats.mTotalTimeForegroundServiceUsed);
XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
@@ -317,6 +343,7 @@
}
XmlUtils.writeIntAttribute(xml, FLAGS_ATTR, event.mFlags);
XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
+ XmlUtils.writeIntAttribute(xml, INSTANCE_ID_ATTR, event.mInstanceId);
switch (event.mEventType) {
case UsageEvents.Event.CONFIGURATION_CHANGE:
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 94d7dbb..5128ae1 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -16,10 +16,18 @@
package com.android.server.usage;
+import static android.app.usage.UsageStatsManager.INTERVAL_BEST;
+import static android.app.usage.UsageStatsManager.INTERVAL_COUNT;
+import static android.app.usage.UsageStatsManager.INTERVAL_DAILY;
+import static android.app.usage.UsageStatsManager.INTERVAL_MONTHLY;
+import static android.app.usage.UsageStatsManager.INTERVAL_WEEKLY;
+import static android.app.usage.UsageStatsManager.INTERVAL_YEARLY;
+
import android.app.usage.ConfigurationStats;
import android.app.usage.EventList;
import android.app.usage.EventStats;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
@@ -29,6 +37,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.SparseIntArray;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.usage.UsageStatsDatabase.StatCombiner;
@@ -65,7 +74,7 @@
private final int mUserId;
// STOPSHIP: Temporary member variable for debugging b/110930764.
- private UsageEvents.Event mLastEvent;
+ private Event mLastEvent;
private static final long[] INTERVAL_LENGTH = new long[] {
UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
@@ -87,7 +96,7 @@
mContext = context;
mDailyExpiryDate = new UnixCalendar(0);
mDatabase = new UsageStatsDatabase(usageStatsDir);
- mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT];
+ mCurrentStats = new IntervalStats[INTERVAL_COUNT];
mListener = listener;
mLogPrefix = "User[" + Integer.toString(userId) + "] ";
mUserId = userId;
@@ -125,28 +134,6 @@
updateRolloverDeadline();
}
- // Now close off any events that were open at the time this was saved.
- for (IntervalStats stat : mCurrentStats) {
- final int pkgCount = stat.packageStats.size();
- for (int i = 0; i < pkgCount; i++) {
- final UsageStats pkgStats = stat.packageStats.valueAt(i);
- if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()
- || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
- if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) {
- stat.update(pkgStats.mPackageName, null, stat.lastTimeSaved,
- UsageEvents.Event.END_OF_DAY);
- }
- if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
- stat.update(pkgStats.mPackageName, null , stat.lastTimeSaved,
- UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE);
- }
- notifyStatsChanged();
- }
- }
-
- stat.updateConfigurationStats(null, stat.lastTimeSaved);
- }
-
if (mDatabase.isNewUpdate()) {
notifyNewUpdate();
}
@@ -158,40 +145,46 @@
loadActiveStats(newTime);
}
- void reportEvent(UsageEvents.Event event) {
+ void reportEvent(Event event) {
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
+ "[" + event.mTimeStamp + "]: "
+ eventToString(event.mEventType));
}
- mLastEvent = new UsageEvents.Event(event);
+ mLastEvent = new Event(event);
if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
// Need to rollover
rolloverStats(event.mTimeStamp);
}
- final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
+ final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
final Configuration newFullConfig = event.mConfiguration;
- if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE &&
- currentDailyStats.activeConfiguration != null) {
+ if (event.mEventType == Event.CONFIGURATION_CHANGE
+ && currentDailyStats.activeConfiguration != null) {
// Make the event configuration a delta.
event.mConfiguration = Configuration.generateDelta(
currentDailyStats.activeConfiguration, newFullConfig);
}
- if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) {
+ if (event.mEventType != Event.SYSTEM_INTERACTION
+ // ACTIVITY_DESTROYED is a private event. If there is preceding ACTIVITY_STOPPED
+ // ACTIVITY_DESTROYED will be dropped. Otherwise it will be converted to
+ // ACTIVITY_STOPPED.
+ && event.mEventType != Event.ACTIVITY_DESTROYED
+ // FLUSH_TO_DISK is a private event.
+ && event.mEventType != Event.FLUSH_TO_DISK) {
currentDailyStats.addEvent(event);
}
boolean incrementAppLaunch = false;
- if (event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
+ if (event.mEventType == Event.ACTIVITY_RESUMED) {
if (event.mPackage != null && !event.mPackage.equals(mLastBackgroundedPackage)) {
incrementAppLaunch = true;
}
- } else if (event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND) {
+ } else if (event.mEventType == Event.ACTIVITY_PAUSED) {
if (event.mPackage != null) {
mLastBackgroundedPackage = event.mPackage;
}
@@ -199,10 +192,10 @@
for (IntervalStats stats : mCurrentStats) {
switch (event.mEventType) {
- case UsageEvents.Event.CONFIGURATION_CHANGE: {
+ case Event.CONFIGURATION_CHANGE: {
stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
} break;
- case UsageEvents.Event.CHOOSER_ACTION: {
+ case Event.CHOOSER_ACTION: {
stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
String[] annotations = event.mContentAnnotations;
if (annotations != null) {
@@ -211,21 +204,21 @@
}
}
} break;
- case UsageEvents.Event.SCREEN_INTERACTIVE: {
+ case Event.SCREEN_INTERACTIVE: {
stats.updateScreenInteractive(event.mTimeStamp);
} break;
- case UsageEvents.Event.SCREEN_NON_INTERACTIVE: {
+ case Event.SCREEN_NON_INTERACTIVE: {
stats.updateScreenNonInteractive(event.mTimeStamp);
} break;
- case UsageEvents.Event.KEYGUARD_SHOWN: {
+ case Event.KEYGUARD_SHOWN: {
stats.updateKeyguardShown(event.mTimeStamp);
} break;
- case UsageEvents.Event.KEYGUARD_HIDDEN: {
+ case Event.KEYGUARD_HIDDEN: {
stats.updateKeyguardHidden(event.mTimeStamp);
} break;
default: {
stats.update(event.mPackage, event.getClassName(),
- event.mTimeStamp, event.mEventType);
+ event.mTimeStamp, event.mEventType, event.mInstanceId);
if (incrementAppLaunch) {
stats.incrementAppLaunchCount(event.mPackage);
}
@@ -286,12 +279,12 @@
*/
private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
StatCombiner<T> combiner) {
- if (intervalType == UsageStatsManager.INTERVAL_BEST) {
+ if (intervalType == INTERVAL_BEST) {
intervalType = mDatabase.findBestFitBucket(beginTime, endTime);
if (intervalType < 0) {
// Nothing saved to disk yet, so every stat is just as equal (no rollover has
// occurred.
- intervalType = UsageStatsManager.INTERVAL_DAILY;
+ intervalType = INTERVAL_DAILY;
}
}
@@ -316,10 +309,10 @@
}
// STOPSHIP: Temporary logging for b/110930764.
- if (intervalType == UsageStatsManager.INTERVAL_DAILY
+ if (intervalType == INTERVAL_DAILY
&& mLastEvent != null && mLastEvent.mTimeStamp >= beginTime) {
final IntervalStats diskStats = mDatabase.getLatestUsageStats(
- UsageStatsManager.INTERVAL_DAILY);
+ INTERVAL_DAILY);
StringBuilder sb = new StringBuilder(256);
sb.append("Last 24 hours of UsageStats missing! timeRange : ");
sb.append(beginTime);
@@ -395,11 +388,11 @@
UsageEvents queryEvents(final long beginTime, final long endTime,
boolean obfuscateInstantApps) {
final ArraySet<String> names = new ArraySet<>();
- List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
- beginTime, endTime, new StatCombiner<UsageEvents.Event>() {
+ List<Event> results = queryStats(INTERVAL_DAILY,
+ beginTime, endTime, new StatCombiner<Event>() {
@Override
public void combine(IntervalStats stats, boolean mutable,
- List<UsageEvents.Event> accumulatedResult) {
+ List<Event> accumulatedResult) {
if (stats.events == null) {
return;
}
@@ -411,11 +404,13 @@
return;
}
- UsageEvents.Event event = stats.events.get(i);
+ Event event = stats.events.get(i);
if (obfuscateInstantApps) {
event = event.getObfuscatedIfInstantApp();
}
- names.add(event.mPackage);
+ if (event.mPackage != null) {
+ names.add(event.mPackage);
+ }
if (event.mClass != null) {
names.add(event.mClass);
}
@@ -437,7 +432,7 @@
final String packageName) {
final ArraySet<String> names = new ArraySet<>();
names.add(packageName);
- final List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
+ final List<Event> results = queryStats(INTERVAL_DAILY,
beginTime, endTime, (stats, mutable, accumulatedResult) -> {
if (stats.events == null) {
return;
@@ -450,7 +445,7 @@
return;
}
- final UsageEvents.Event event = stats.events.get(i);
+ final Event event = stats.events.get(i);
if (!packageName.equals(event.mPackage)) {
continue;
}
@@ -492,33 +487,33 @@
// Make a note of which components need a new CONTINUE_PREVIOUS_DAY or
// CONTINUING_FOREGROUND_SERVICE entry.
final Configuration previousConfig =
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
- ArraySet<String> continuePreviousDay = new ArraySet<>();
- ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundActivity =
+ mCurrentStats[INTERVAL_DAILY].activeConfiguration;
+ ArraySet<String> continuePkgs = new ArraySet<>();
+ ArrayMap<String, SparseIntArray> continueActivity =
new ArrayMap<>();
- ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundService =
+ ArrayMap<String, ArrayMap<String, Integer>> continueForegroundService =
new ArrayMap<>();
for (IntervalStats stat : mCurrentStats) {
final int pkgCount = stat.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
final UsageStats pkgStats = stat.packageStats.valueAt(i);
- if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()
- || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
- if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) {
- continuePreviousDayForegroundActivity.put(pkgStats.mPackageName,
- pkgStats.mLastForegroundActivityEventMap);
+ if (pkgStats.mActivities.size() > 0
+ || !pkgStats.mForegroundServices.isEmpty()) {
+ if (pkgStats.mActivities.size() > 0) {
+ continueActivity.put(pkgStats.mPackageName,
+ pkgStats.mActivities);
stat.update(pkgStats.mPackageName, null,
mDailyExpiryDate.getTimeInMillis() - 1,
- UsageEvents.Event.END_OF_DAY);
+ Event.END_OF_DAY, 0);
}
- if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
- continuePreviousDayForegroundService.put(pkgStats.mPackageName,
- pkgStats.mLastForegroundServiceEventMap);
+ if (!pkgStats.mForegroundServices.isEmpty()) {
+ continueForegroundService.put(pkgStats.mPackageName,
+ pkgStats.mForegroundServices);
stat.update(pkgStats.mPackageName, null,
mDailyExpiryDate.getTimeInMillis() - 1,
- UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE);
+ Event.ROLLOVER_FOREGROUND_SERVICE, 0);
}
- continuePreviousDay.add(pkgStats.mPackageName);
+ continuePkgs.add(pkgStats.mPackageName);
notifyStatsChanged();
}
}
@@ -532,27 +527,27 @@
mDatabase.prune(currentTimeMillis);
loadActiveStats(currentTimeMillis);
- final int continueCount = continuePreviousDay.size();
+ final int continueCount = continuePkgs.size();
for (int i = 0; i < continueCount; i++) {
- String pkgName = continuePreviousDay.valueAt(i);
- final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime;
+ String pkgName = continuePkgs.valueAt(i);
+ final long beginTime = mCurrentStats[INTERVAL_DAILY].beginTime;
for (IntervalStats stat : mCurrentStats) {
- if (continuePreviousDayForegroundActivity.containsKey(pkgName)) {
- final ArrayMap<String, Integer> foregroundActivityEventMap =
- continuePreviousDayForegroundActivity.get(pkgName);
- final int size = foregroundActivityEventMap.size();
+ if (continueActivity.containsKey(pkgName)) {
+ final SparseIntArray eventMap =
+ continueActivity.get(pkgName);
+ final int size = eventMap.size();
for (int j = 0; j < size; j++) {
- stat.update(pkgName, foregroundActivityEventMap.keyAt(j), beginTime,
- UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+ stat.update(pkgName, null, beginTime,
+ eventMap.valueAt(j), eventMap.keyAt(j));
}
}
- if (continuePreviousDayForegroundService.containsKey(pkgName)) {
- final ArrayMap<String, Integer> foregroundServiceEventMap =
- continuePreviousDayForegroundService.get(pkgName);
- final int size = foregroundServiceEventMap.size();
+ if (continueForegroundService.containsKey(pkgName)) {
+ final ArrayMap<String, Integer> eventMap =
+ continueForegroundService.get(pkgName);
+ final int size = eventMap.size();
for (int j = 0; j < size; j++) {
- stat.update(pkgName, foregroundServiceEventMap.keyAt(j), beginTime,
- UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE);
+ stat.update(pkgName, eventMap.keyAt(j), beginTime,
+ eventMap.valueAt(j), 0);
}
}
stat.updateConfigurationStats(previousConfig, beginTime);
@@ -611,7 +606,7 @@
private void updateRolloverDeadline() {
mDailyExpiryDate.setTimeInMillis(
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
+ mCurrentStats[INTERVAL_DAILY].beginTime);
mDailyExpiryDate.addDays(1);
Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
@@ -660,7 +655,7 @@
}
- void printEvent(IndentingPrintWriter pw, UsageEvents.Event event, boolean prettyDates) {
+ void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) {
pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates));
pw.printPair("type", eventToString(event.mEventType));
pw.printPair("package", event.mPackage);
@@ -673,10 +668,15 @@
if (event.mShortcutId != null) {
pw.printPair("shortcutId", event.mShortcutId);
}
- if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
+ if (event.mEventType == Event.STANDBY_BUCKET_CHANGED) {
pw.printPair("standbyBucket", event.getStandbyBucket());
pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
+ } else if (event.mEventType == Event.ACTIVITY_RESUMED
+ || event.mEventType == Event.ACTIVITY_PAUSED
+ || event.mEventType == Event.ACTIVITY_STOPPED) {
+ pw.printPair("instanceId", event.getInstanceId());
}
+
if (event.mNotificationChannelId != null) {
pw.printPair("channelId", event.mNotificationChannelId);
}
@@ -691,11 +691,11 @@
final long beginTime = yesterday.getTimeInMillis();
- List<UsageEvents.Event> events = queryStats(UsageStatsManager.INTERVAL_DAILY,
- beginTime, endTime, new StatCombiner<UsageEvents.Event>() {
+ List<Event> events = queryStats(INTERVAL_DAILY,
+ beginTime, endTime, new StatCombiner<Event>() {
@Override
public void combine(IntervalStats stats, boolean mutable,
- List<UsageEvents.Event> accumulatedResult) {
+ List<Event> accumulatedResult) {
if (stats.events == null) {
return;
}
@@ -707,7 +707,7 @@
return;
}
- UsageEvents.Event event = stats.events.get(i);
+ Event event = stats.events.get(i);
if (pkg != null && !pkg.equals(event.mPackage)) {
continue;
}
@@ -727,7 +727,7 @@
pw.println(")");
if (events != null) {
pw.increaseIndent();
- for (UsageEvents.Event event : events) {
+ for (Event event : events) {
printEvent(pw, event, prettyDates);
}
pw.decreaseIndent();
@@ -772,9 +772,17 @@
continue;
}
pw.printPair("package", usageStats.mPackageName);
- pw.printPair("totalTime",
+ pw.printPair("totalTimeUsed",
formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
- pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
+ pw.printPair("lastTimeUsed", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
+ pw.printPair("totalTimeVisible",
+ formatElapsedTime(usageStats.mTotalTimeVisible, prettyDates));
+ pw.printPair("lastTimeVisible",
+ formatDateTime(usageStats.mLastTimeVisible, prettyDates));
+ pw.printPair("totalTimeFS",
+ formatElapsedTime(usageStats.mTotalTimeForegroundServiceUsed, prettyDates));
+ pw.printPair("lastTimeFS",
+ formatDateTime(usageStats.mLastTimeForegroundServiceUsed, prettyDates));
pw.printPair("appLaunchCount", usageStats.mAppLaunchCount);
pw.println();
}
@@ -845,7 +853,7 @@
final EventList events = stats.events;
final int eventCount = events != null ? events.size() : 0;
for (int i = 0; i < eventCount; i++) {
- final UsageEvents.Event event = events.get(i);
+ final Event event = events.get(i);
if (pkg != null && !pkg.equals(event.mPackage)) {
continue;
}
@@ -858,13 +866,13 @@
private static String intervalToString(int interval) {
switch (interval) {
- case UsageStatsManager.INTERVAL_DAILY:
+ case INTERVAL_DAILY:
return "daily";
- case UsageStatsManager.INTERVAL_WEEKLY:
+ case INTERVAL_WEEKLY:
return "weekly";
- case UsageStatsManager.INTERVAL_MONTHLY:
+ case INTERVAL_MONTHLY:
return "monthly";
- case UsageStatsManager.INTERVAL_YEARLY:
+ case INTERVAL_YEARLY:
return "yearly";
default:
return "?";
@@ -873,47 +881,49 @@
private static String eventToString(int eventType) {
switch (eventType) {
- case UsageEvents.Event.NONE:
+ case Event.NONE:
return "NONE";
- case UsageEvents.Event.MOVE_TO_BACKGROUND:
- return "MOVE_TO_BACKGROUND";
- case UsageEvents.Event.MOVE_TO_FOREGROUND:
- return "MOVE_TO_FOREGROUND";
- case UsageEvents.Event.FOREGROUND_SERVICE_START:
+ case Event.ACTIVITY_PAUSED:
+ return "ACTIVITY_PAUSED";
+ case Event.ACTIVITY_RESUMED:
+ return "ACTIVITY_RESUMED";
+ case Event.FOREGROUND_SERVICE_START:
return "FOREGROUND_SERVICE_START";
- case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
+ case Event.FOREGROUND_SERVICE_STOP:
return "FOREGROUND_SERVICE_STOP";
- case UsageEvents.Event.END_OF_DAY:
+ case Event.ACTIVITY_STOPPED:
+ return "ACTIVITY_STOPPED";
+ case Event.END_OF_DAY:
return "END_OF_DAY";
- case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE:
+ case Event.ROLLOVER_FOREGROUND_SERVICE:
return "ROLLOVER_FOREGROUND_SERVICE";
- case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
+ case Event.CONTINUE_PREVIOUS_DAY:
return "CONTINUE_PREVIOUS_DAY";
- case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE:
+ case Event.CONTINUING_FOREGROUND_SERVICE:
return "CONTINUING_FOREGROUND_SERVICE";
- case UsageEvents.Event.CONFIGURATION_CHANGE:
+ case Event.CONFIGURATION_CHANGE:
return "CONFIGURATION_CHANGE";
- case UsageEvents.Event.SYSTEM_INTERACTION:
+ case Event.SYSTEM_INTERACTION:
return "SYSTEM_INTERACTION";
- case UsageEvents.Event.USER_INTERACTION:
+ case Event.USER_INTERACTION:
return "USER_INTERACTION";
- case UsageEvents.Event.SHORTCUT_INVOCATION:
+ case Event.SHORTCUT_INVOCATION:
return "SHORTCUT_INVOCATION";
- case UsageEvents.Event.CHOOSER_ACTION:
+ case Event.CHOOSER_ACTION:
return "CHOOSER_ACTION";
- case UsageEvents.Event.NOTIFICATION_SEEN:
+ case Event.NOTIFICATION_SEEN:
return "NOTIFICATION_SEEN";
- case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ case Event.STANDBY_BUCKET_CHANGED:
return "STANDBY_BUCKET_CHANGED";
- case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ case Event.NOTIFICATION_INTERRUPTION:
return "NOTIFICATION_INTERRUPTION";
- case UsageEvents.Event.SLICE_PINNED:
+ case Event.SLICE_PINNED:
return "SLICE_PINNED";
- case UsageEvents.Event.SLICE_PINNED_PRIV:
+ case Event.SLICE_PINNED_PRIV:
return "SLICE_PINNED_PRIV";
- case UsageEvents.Event.SCREEN_INTERACTIVE:
+ case Event.SCREEN_INTERACTIVE:
return "SCREEN_INTERACTIVE";
- case UsageEvents.Event.SCREEN_NON_INTERACTIVE:
+ case Event.SCREEN_NON_INTERACTIVE:
return "SCREEN_NON_INTERACTIVE";
case UsageEvents.Event.KEYGUARD_SHOWN:
return "KEYGUARD_SHOWN";
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9f0bdd7..49030f5 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -460,6 +460,12 @@
"android.telecom.extra.START_CALL_WITH_RTT";
/**
+ * A boolean extra set to indicate whether an app is eligible to be bound to when there are
+ * ongoing calls on the device.
+ */
+ public static final String EXTRA_IS_ENABLED = "android.telecom.extra.IS_ENABLED";
+
+ /**
* A boolean meta-data value indicating whether an {@link InCallService} implements an
* in-call user interface. Dialer implementations (see {@link #getDefaultDialerPackage()}) which
* would also like to replace the in-call interface should set this meta-data to {@code true} in
@@ -471,8 +477,6 @@
* A boolean meta-data value indicating whether an {@link InCallService} implements an
* in-call user interface to be used while the device is in car-mode (see
* {@link android.content.res.Configuration.UI_MODE_TYPE_CAR}).
- *
- * @hide
*/
public static final String METADATA_IN_CALL_SERVICE_CAR_MODE_UI =
"android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
@@ -1462,7 +1466,6 @@
* otherwise.
*/
@RequiresPermission(Manifest.permission.ANSWER_PHONE_CALLS)
- @SystemApi
public boolean endCall() {
try {
if (isServiceConnected()) {
@@ -1539,7 +1542,6 @@
/**
* Returns whether TTY is supported on this device.
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE
@@ -2040,7 +2042,6 @@
} catch (RemoteException e) {
Log.e(TAG, "RemoteException handleCallIntent: " + e);
}
-
}
private ITelecomService getTelecomService() {
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 143b323..d64efea 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -305,6 +305,8 @@
*/
void handleCallIntent(in Intent intent);
+ void setTestDefaultCallRedirectionApp(String packageName);
+
void setTestDefaultCallScreeningApp(String packageName);
void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 6a396ce..1cbe5a2 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -3640,8 +3640,9 @@
/**
* Generates a content {@link Uri} used to receive updates on precise carrier identity
- * change on the given subscriptionId
- * {@link TelephonyManager#ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED}.
+ * change on the given subscriptionId returned by
+ * {@link TelephonyManager#getSimPreciseCarrierId()}.
+ * @see TelephonyManager#ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED
* <p>
* Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
* precise carrier identity {@link TelephonyManager#getSimPreciseCarrierId()}
@@ -3652,7 +3653,6 @@
*
* @param subscriptionId the subscriptionId to receive updates on
* @return the Uri used to observe precise carrier identity changes
- * @hide
*/
public static Uri getPreciseCarrierIdUriForSubscriptionId(int subscriptionId) {
return Uri.withAppendedPath(Uri.withAppendedPath(CONTENT_URI, "precise"),
@@ -3674,24 +3674,22 @@
public static final String CARRIER_ID = "carrier_id";
/**
- * A user facing carrier name for precise carrier id.
- * @see TelephonyManager#getSimPreciseCarrierIdName()
- * This is not a database column, only used to notify content observers for
- * {@link #getPreciseCarrierIdUriForSubscriptionId(int)}
- * @hide
- */
- public static final String PRECISE_CARRIER_ID_NAME = "precise_carrier_id_name";
-
- /**
* A fine-grained carrier id.
* @see TelephonyManager#getSimPreciseCarrierId()
* This is not a database column, only used to notify content observers for
* {@link #getPreciseCarrierIdUriForSubscriptionId(int)}
- * @hide
*/
public static final String PRECISE_CARRIER_ID = "precise_carrier_id";
/**
+ * A user facing carrier name for precise carrier id {@link #PRECISE_CARRIER_ID}.
+ * @see TelephonyManager#getSimPreciseCarrierIdName()
+ * This is not a database column, only used to notify content observers for
+ * {@link #getPreciseCarrierIdUriForSubscriptionId(int)}
+ */
+ public static final String PRECISE_CARRIER_ID_NAME = "precise_carrier_id_name";
+
+ /**
* A unique parent carrier id. The parent-child
* relationship can be used to further differentiate a single carrier by different networks,
* by prepaid v.s. postpaid or even by 4G v.s. 3G plan. It's an optional field.
@@ -3703,18 +3701,6 @@
public static final String PARENT_CARRIER_ID = "parent_carrier_id";
/**
- * A unique mno carrier id. mno carrier shares the same {@link All#MCCMNC} as carrier id
- * and can be solely identified by {@link All#MCCMNC} only. If there is no such mno
- * carrier, then mno carrier id equals to {@link #CARRIER_ID carrier id}.
- *
- * <p>mno carrier id can be used as fallback id. When the exact carrier id configurations
- * are not found, usually fall back to its mno carrier id.
- * <P>Type: INTEGER </P>
- * @hide
- */
- public static final String MNO_CARRIER_ID = "mno_carrier_id";
-
- /**
* Contains mappings between matching rules with carrier id for all carriers.
* @hide
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 60eb18c..cfe134fd 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -68,7 +68,13 @@
* This intent is broadcast by the system when carrier config changes. An int is specified in
* {@link #EXTRA_SLOT_INDEX} to indicate the slot index that this is for. An optional int extra
* {@link #EXTRA_SUBSCRIPTION_INDEX} is included to indicate the subscription index if a valid
- * one is available for the slot index.
+ * one is available for the slot index. An optional int extra
+ * {@link TelephonyManager#EXTRA_CARRIER_ID} is included to indicate the carrier id for the
+ * changed carrier configuration. An optional int extra
+ * {@link TelephonyManager#EXTRA_PRECISE_CARRIER_ID} is included to indicate the precise
+ * carrier id for the changed carrier configuration.
+ * @see TelephonyManager#getSimCarrierId()
+ * @see TelephonyManager#getSimPreciseCarrierId()
*/
public static final String
ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
@@ -81,7 +87,6 @@
* Specifies a value that identifies the version of the carrier configuration that is
* currently in use. This string is displayed on the UI.
* The format of the string is not specified.
- * @hide
*/
public static final String KEY_CARRIER_CONFIG_VERSION_STRING =
"carrier_config_version_string";
@@ -403,7 +408,6 @@
* @see SubscriptionManager#getSubscriptionPlans(int)
* @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
*/
- @SystemApi
public static final String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING =
"config_plans_package_override_string";
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index ea408bf..1b37bad 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -365,7 +365,6 @@
*
* @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
*/
- @SystemApi
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(allOf = {
android.Manifest.permission.MODIFY_PHONE_STATE,
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2c06c47..387453f 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -19,6 +19,7 @@
import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
@@ -52,6 +53,7 @@
import android.os.ServiceManager;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -67,6 +69,7 @@
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* SubscriptionManager is the application interface to SubscriptionController
@@ -633,7 +636,6 @@
* the user is interested in.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- @SystemApi
public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS
= "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
@@ -653,7 +655,6 @@
* {@code android.permission.MANAGE_SUBSCRIPTION_PLANS} permission.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @SystemApi
public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS
= "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
@@ -2059,7 +2060,6 @@
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
- @SystemApi
public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
try {
SubscriptionPlan[] subscriptionPlans =
@@ -2091,7 +2091,6 @@
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
- @SystemApi
public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
try {
getNetworkPolicy().setSubscriptionPlans(subId,
@@ -2133,7 +2132,6 @@
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
- @SystemApi
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@DurationMillisLong long timeoutMillis) {
try {
@@ -2169,7 +2167,6 @@
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
- @SystemApi
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
@DurationMillisLong long timeoutMillis) {
try {
@@ -2405,16 +2402,21 @@
* together, some of them may be invisible to the users, etc.
*
* Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
- * permission or can manage all subscriptions in the list, according to their
- * acess rules.
+ * permission or had carrier privilege permission on the subscriptions:
+ * {@link TelephonyManager#hasCarrierPrivileges()} or
+ * {@link #canManageSubscription(SubscriptionInfo)}
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
*
* @param subIdList list of subId that will be in the same group
* @return groupUUID a UUID assigned to the subscription group. It returns
* null if fails.
*
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public String setSubscriptionGroup(int[] subIdList) {
+ public @Nullable String setSubscriptionGroup(@NonNull int[] subIdList) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
if (VDBG) {
logd("[setSubscriptionGroup]+ subIdList:" + Arrays.toString(subIdList));
@@ -2434,6 +2436,80 @@
}
/**
+ * Remove a list of subscriptions from their subscription group.
+ * See {@link #setSubscriptionGroup(int[])} for more details.
+ *
+ * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ * permission or had carrier privilege permission on the subscriptions:
+ * {@link TelephonyManager#hasCarrierPrivileges()} or
+ * {@link #canManageSubscription(SubscriptionInfo)}
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
+ *
+ * @param subIdList list of subId that need removing from their groups.
+ * @return whether the operation succeeds.
+ *
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean removeSubscriptionsFromGroup(@NonNull int[] subIdList) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (VDBG) {
+ logd("[removeSubscriptionsFromGroup]+ subIdList:" + Arrays.toString(subIdList));
+ }
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ return iSub.removeSubscriptionsFromGroup(subIdList, pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return false;
+ }
+
+ /**
+ * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
+ * See {@link #setSubscriptionGroup(int[])} for more details.
+ *
+ * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE}
+ * permission or had carrier privilege permission on the subscription.
+ * {@link TelephonyManager#hasCarrierPrivileges()}
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
+ *
+ * @param subId of which list of subInfo from the same group will be returned.
+ * @return list of subscriptionInfo that belong to the same group, including the given
+ * subscription itself. It will return null if the subscription doesn't exist or it
+ * doesn't belong to any group.
+ *
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public @Nullable List<SubscriptionInfo> getSubscriptionsInGroup(int subId) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (VDBG) {
+ logd("[getSubscriptionsInGroup]+ subId:" + subId);
+ }
+
+ List<SubscriptionInfo> result = null;
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getSubscriptionsInGroup(subId, pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+ }
+
+ /**
* Set metered by simInfo index
*
* @param isMetered whether it’s a metered subscription.
@@ -2448,6 +2524,42 @@
(iSub)-> iSub.setMetered(isMetered, subId));
}
+ /**
+ * Whether system UI should hide a subscription. If it's a bundled opportunistic
+ * subscription, it shouldn't show up in anywhere in Settings app, dialer app,
+ * or status bar.
+ *
+ * @param info the subscriptionInfo to check against.
+ * @return true if this subscription should be hidden.
+ *
+ * @hide
+ */
+ public static boolean shouldHideSubscription(SubscriptionInfo info) {
+ return (info != null && !TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic());
+ }
+
+ /**
+ * Return a list of subscriptions that are available and visible to the user.
+ * Used by Settings app to show a list of subscriptions for user to pick.
+ *
+ * <p>
+ * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required
+ * for getSelectableSubscriptionInfoList to be invoked.
+ * @return list of user selectable subscriptions.
+ *
+ * @hide
+ */
+ public @Nullable List<SubscriptionInfo> getSelectableSubscriptionInfoList() {
+ List<SubscriptionInfo> availableList = getAvailableSubscriptionInfoList();
+ if (availableList == null) {
+ return null;
+ } else {
+ return getAvailableSubscriptionInfoList().stream()
+ .filter(subInfo -> !shouldHideSubscription(subInfo))
+ .collect(Collectors.toList());
+ }
+ }
+
private interface CallISubMethodHelper {
int callMethod(ISub iSub) throws RemoteException;
}
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
index e8bbe42..d67169c 100644
--- a/telephony/java/android/telephony/SubscriptionPlan.java
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -45,7 +45,6 @@
* @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
* @see SubscriptionManager#getSubscriptionPlans(int)
*/
-@SystemApi
public final class SubscriptionPlan implements Parcelable {
/** {@hide} */
@IntDef(prefix = "LIMIT_BEHAVIOR_", value = {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 422e66d..c025090 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1217,81 +1217,79 @@
"android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
/**
- * Broadcast Action: The subscription precise carrier identity has changed.
- * Similar like {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED}, this intent will be sent
- * on the event of {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED}. However, its possible
- * that precise carrier identity changes while
- * {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} remains the same e.g, the same
- * subscription switches to different IMSI could potentially change its precise carrier id.
- *
- * The intent will have the following extra values:
- * <ul>
- * <li>{@link #EXTRA_PRECISE_CARRIER_ID} The up-to-date precise carrier id of the
- * current subscription.
- * </li>
- * <li>{@link #EXTRA_PRECISE_CARRIER_NAME} The up-to-date carrier name of the current
- * subscription.
- * </li>
- * <li>{@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier
- * identity.
- * </li>
- * </ul>
- * <p class="note">This is a protected intent that can only be sent by the system.
- * @hide
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED =
- "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED";
-
- /**
* An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
- * the updated carrier id {@link TelephonyManager#getSimCarrierId()} of
- * the current subscription.
+ * the updated carrier id returned by {@link TelephonyManager#getSimCarrierId()}.
* <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
* the carrier cannot be identified.
*/
public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
/**
- * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
- * the updated mno carrier id of the current subscription.
- * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
- * the carrier cannot be identified.
- *
- *@hide
- */
- public static final String EXTRA_MNO_CARRIER_ID = "android.telephony.extra.MNO_CARRIER_ID";
-
- /**
* An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
* indicates the updated carrier name of the current subscription.
- * {@see TelephonyManager#getSimCarrierIdName()}
+ * @see TelephonyManager#getSimCarrierIdName()
* <p>Carrier name is a user-facing name of the carrier id {@link #EXTRA_CARRIER_ID},
* usually the brand name of the subsidiary (e.g. T-Mobile).
*/
public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
/**
+ * Broadcast Action: The subscription precise carrier identity has changed.
+ * The precise carrier id can be used to further differentiate a carrier by different
+ * networks, by prepaid v.s.postpaid or even by 4G v.s.3G plan. Each carrier has a unique
+ * carrier id returned by {@link #getSimCarrierId()} but could have multiple precise carrier id.
+ * e.g, {@link #getSimCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM,
+ * while {@link #getSimPreciseCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based
+ * on the current subscription IMSI. For carriers without any fine-grained ids, precise carrier
+ * id is same as carrier id.
+ *
+ * <p>Similar like {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED}, this intent will be
+ * sent on the event of {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} while its also
+ * possible to be sent without {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} when
+ * precise carrier id changes with the same carrier id.
+ * e.g, the same subscription switches to different IMSI could potentially change its
+ * precise carrier id while carrier id remains the same.
+ * @see #getSimPreciseCarrierId()
+ * @see #getSimCarrierId()
+ *
+ * The intent will have the following extra values:
+ * <ul>
+ * <li>{@link #EXTRA_PRECISE_CARRIER_ID} The up-to-date precise carrier id of the
+ * current subscription.
+ * </li>
+ * <li>{@link #EXTRA_PRECISE_CARRIER_NAME} The up-to-date name of the precise carrier id.
+ * </li>
+ * <li>{@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier
+ * identity.
+ * </li>
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent by the system.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED =
+ "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED";
+
+ /**
* An int extra used with {@link #ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED} which
- * indicates the updated precise carrier id {@link TelephonyManager#getSimPreciseCarrierId()} of
- * the current subscription. Note, its possible precise carrier id changes while
- * {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} remains the same e.g, when
- * subscription switch to different IMSI.
+ * indicates the updated precise carrier id returned by
+ * {@link TelephonyManager#getSimPreciseCarrierId()}. Note, its possible precise carrier id
+ * changes while {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} remains the same
+ * e.g, when subscription switch to different IMSIs.
* <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
* the carrier cannot be identified.
- * @hide
*/
public static final String EXTRA_PRECISE_CARRIER_ID =
"android.telephony.extra.PRECISE_CARRIER_ID";
/**
* An string extra used with {@link #ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED} which
- * indicates the updated precise carrier name of the current subscription.
- * {@see TelephonyManager#getSimPreciseCarrierIdName()}
- * <p>it's a user-facing name of the precise carrier id {@link #EXTRA_PRECISE_CARRIER_ID},
- * @hide
+ * indicates the updated precise carrier name returned by
+ * {@link TelephonyManager#getSimPreciseCarrierIdName()}.
+ * <p>it's a user-facing name of the precise carrier id {@link #EXTRA_PRECISE_CARRIER_ID}, e.g,
+ * Tracfone-AT&T.
*/
- public static final String EXTRA_PRECISE_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
+ public static final String EXTRA_PRECISE_CARRIER_NAME =
+ "android.telephony.extra.PRECISE_CARRIER_NAME";
/**
* An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} to indicate the
@@ -6632,7 +6630,7 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getCarrierPrivilegeStatus(mSubId) ==
+ return telephony.getCarrierPrivilegeStatus(subId) ==
CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
}
} catch (RemoteException ex) {
@@ -8518,7 +8516,7 @@
/**
* Returns carrier id name of the current subscription.
- * <p>Carrier id name is a user-facing name of carrier id
+ * <p>Carrier id name is a user-facing name of carrier id returned by
* {@link #getSimCarrierId()}, usually the brand name of the subsidiary
* (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but
* should have a single carrier name. Carrier name is not a canonical identity,
@@ -8528,7 +8526,7 @@
* @return Carrier name of the current subscription. Return {@code null} if the subscription is
* unavailable or the carrier cannot be identified.
*/
- public CharSequence getSimCarrierIdName() {
+ public @Nullable CharSequence getSimCarrierIdName() {
try {
ITelephony service = getITelephony();
if (service != null) {
@@ -8545,10 +8543,10 @@
*
* <p>The precise carrier id can be used to further differentiate a carrier by different
* networks, by prepaid v.s.postpaid or even by 4G v.s.3G plan. Each carrier has a unique
- * carrier id {@link #getSimCarrierId()} but can have multiple precise carrier id. e.g,
- * {@link #getSimCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM, while
- * {@link #getSimPreciseCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based on the
- * current subscription IMSI.
+ * carrier id returned by {@link #getSimCarrierId()} but could have multiple precise carrier id.
+ * e.g, {@link #getSimCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM,
+ * while {@link #getSimPreciseCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based
+ * on the current subscription IMSI.
*
* <p>For carriers without any fine-grained carrier ids, return {@link #getSimCarrierId()}
* <p>Precise carrier ids are defined in the same way as carrier id
@@ -8558,8 +8556,6 @@
* @return Returns fine-grained carrier id of the current subscription.
* Return {@link #UNKNOWN_CARRIER_ID} if the subscription is unavailable or the carrier cannot
* be identified.
- *
- * @hide
*/
public int getSimPreciseCarrierId() {
try {
@@ -8575,16 +8571,14 @@
/**
* Similar like {@link #getSimCarrierIdName()}, returns user-facing name of the
- * precise carrier id {@link #getSimPreciseCarrierId()}
+ * precise carrier id returned by {@link #getSimPreciseCarrierId()}.
*
* <p>The returned name is unlocalized.
*
* @return user-facing name of the subscription precise carrier id. Return {@code null} if the
* subscription is unavailable or the carrier cannot be identified.
- *
- * @hide
*/
- public CharSequence getSimPreciseCarrierIdName() {
+ public @Nullable CharSequence getSimPreciseCarrierIdName() {
try {
ITelephony service = getITelephony();
if (service != null) {
@@ -8597,6 +8591,62 @@
}
/**
+ * Returns carrier id based on sim MCCMNC (returned by {@link #getSimOperator()}) only.
+ * This is used for fallback when configurations/logic for exact carrier id
+ * {@link #getSimCarrierId()} are not found.
+ *
+ * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
+ * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
+ * by default. After carrier id table update, a new carrier id was assigned. If apps don't
+ * take the update with the new id, it might be helpful to always fallback by using carrier
+ * id based on MCCMNC if there is no match.
+ *
+ * @return matching carrier id from sim MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the
+ * subscription is unavailable or the carrier cannot be identified.
+ */
+ public int getCarrierIdFromSimMccMnc() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getCarrierIdFromMccMnc(getSlotIndex(), getSimOperator(), true);
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ return UNKNOWN_CARRIER_ID;
+ }
+
+ /**
+ * Returns carrier id based on MCCMNC (returned by {@link #getSimOperator()}) only. This is
+ * used for fallback when configurations/logic for exact carrier id {@link #getSimCarrierId()}
+ * are not found.
+ *
+ * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
+ * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
+ * by default. After carrier id table update, a new carrier id was assigned. If apps don't
+ * take the update with the new id, it might be helpful to always fallback by using carrier
+ * id based on MCCMNC if there is no match.
+ *
+ * @return matching carrier id from passing MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the
+ * subscription is unavailable or the carrier cannot be identified.
+ * @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, false);
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ return UNKNOWN_CARRIER_ID;
+ }
+
+ /**
* Return a list of certs in hex string from loaded carrier privileges access rules.
*
* @return a list of certificate in hex string. return {@code null} if there is no certs
@@ -8620,48 +8670,6 @@
}
/**
- * Returns MNO carrier id of the current subscription’s MCCMNC.
- * <p>MNO carrier id can be solely identified by subscription mccmnc. This is mainly used
- * for MNO fallback when exact carrier id {@link #getSimCarrierId()}
- * configurations are not found.
- *
- * @return MNO carrier id of the current subscription. Return the value same as carrier id
- * {@link #getSimCarrierId()}, if MNO carrier id cannot be identified.
- * @hide
- */
- public int getSimMNOCarrierId() {
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- return service.getSubscriptionMNOCarrierId(getSubId());
- }
- } catch (RemoteException ex) {
- // This could happen if binder process crashes.
- }
- 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, represented by the hex
* string. e.g, A00000015141434C00. See ETSI 102.221 and 101.220
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index a1afc08..8d148c3 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -969,7 +969,7 @@
*/
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("[ApnSettingV5] ")
+ sb.append("[ApnSettingV6] ")
.append(mEntryName)
.append(", ").append(mId)
.append(", ").append(mOperatorNumeric)
@@ -996,6 +996,7 @@
sb.append(", ").append(mPermanentFailed);
sb.append(", ").append(mNetworkTypeBitmask);
sb.append(", ").append(mApnSetId);
+ sb.append(", ").append(mCarrierId);
return sb.toString();
}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 4af31b5..bef1142 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -125,7 +125,6 @@
*
* @param result The result code. Must be one of the {@link ResultCode}.
*/
- @SystemApi
public void onSetDataProfileComplete(@ResultCode int result) {
IDataServiceCallback callback = mCallback.get();
if (callback != null) {
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 8fcdb6e..f124595 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -16,11 +16,15 @@
package android.telephony.ims;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This class enables an application to get details on why a method call failed.
*
@@ -30,158 +34,292 @@
public final class ImsReasonInfo implements Parcelable {
/**
- * Specific code of each types
+ * The Reason is unspecified.
*/
public static final int CODE_UNSPECIFIED = 0;
- /**
- * LOCAL
- */
+
+ // LOCAL
+
// IMS -> Telephony
- // The passed argument is an invalid
+ /**
+ * The passed argument is invalid.
+ */
public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101;
- // The operation is invoked in invalid call state
+ /**
+ * The operation was invoked while in an invalid call state.
+ */
public static final int CODE_LOCAL_ILLEGAL_STATE = 102;
- // IMS service internal error
+ /**
+ * IMS service internal error
+ */
public static final int CODE_LOCAL_INTERNAL_ERROR = 103;
- // IMS service goes down (service connection is lost)
+ /**
+ * ImsService has crashed (service connection is lost).
+ */
public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106;
- // No pending incoming call exists
+ /**
+ * No pending incoming call exists
+ */
public static final int CODE_LOCAL_NO_PENDING_CALL = 107;
- // IMS Call ended during conference merge process
+ /**
+ * IMS Call ended during conference merge process
+ */
public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108;
// IMS -> Telephony
- // Service unavailable; by power off
+ /**
+ * Service unavailable; radio power off
+ */
public static final int CODE_LOCAL_POWER_OFF = 111;
- // Service unavailable; by low battery
+ /**
+ * Service unavailable; low battery
+ */
public static final int CODE_LOCAL_LOW_BATTERY = 112;
- // Service unavailable; by out of service (data service state)
+ /**
+ * Service unavailable; out of service (data service state)
+ */
public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121;
- // Service unavailable; by no LTE coverage
- // (VoLTE is not supported even though IMS is registered)
+ /**
+ * Service unavailable; no LTE coverage
+ * (VoLTE is not supported even though IMS is registered)
+ */
public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122;
- // Service unavailable; by located in roaming area
+ /**
+ * Service unavailable; located in roaming area
+ */
public static final int CODE_LOCAL_NETWORK_ROAMING = 123;
- // Service unavailable; by IP changed
+ /**
+ * Service unavailable; IP changed
+ */
public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124;
- // Service unavailable; other
+ /**
+ * Service unavailable; for an unspecified reason
+ */
public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131;
- // Service unavailable; IMS connection is lost (IMS is not registered)
+ /**
+ * Service unavailable; IMS is not registered
+ */
public static final int CODE_LOCAL_NOT_REGISTERED = 132;
// IMS <-> Telephony
- // Max call exceeded
+ /**
+ * Maximum number of simultaneous calls exceeded
+ */
public static final int CODE_LOCAL_CALL_EXCEEDED = 141;
// IMS <- Telephony
- // Call busy
+ /**
+ * The call is busy.
+ */
public static final int CODE_LOCAL_CALL_BUSY = 142;
- // Call decline
+ /**
+ * The Call has been declined locally on this device.
+ */
public static final int CODE_LOCAL_CALL_DECLINE = 143;
// IMS -> Telephony
- // SRVCC is in progress
+ /**
+ * Can not complete call; an SRVCC is in progress.
+ */
public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144;
- // Resource reservation is failed (QoS precondition)
+ /**
+ * Can not complete call; resource reservation is failed (QoS precondition)
+ */
public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145;
- // Retry CS call; VoLTE service can't be provided by the network or remote end
- // Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ /**
+ * VoLTE service can't be provided by the network or remote end, retry the call.
+ * Resolve the extra code provided in (EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ */
public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146;
- // Retry VoLTE call; VoLTE service can't be provided by the network temporarily
+ /**
+ * VoLTE service can't be provided by the network temporarily, retry the call.
+ */
public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147;
- // IMS call is already terminated (in TERMINATED state)
+ /**
+ * IMS call is already terminated (in TERMINATED state).
+ */
public static final int CODE_LOCAL_CALL_TERMINATED = 148;
- // Handover not feasible
+ /**
+ * Call was disconnected because a handover is not feasible due to network conditions.
+ */
public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149;
- /**
+ /*
* TIMEOUT (IMS -> Telephony)
*/
- // 1xx waiting timer is expired after sending INVITE request (MO only)
+ /**
+ * 1xx waiting timer is expired after sending INVITE request (MO calls only)
+ */
public static final int CODE_TIMEOUT_1XX_WAITING = 201;
- // User no answer during call setup operation (MO/MT)
- // MO : 200 OK to INVITE request is not received,
- // MT : No action from user after alerting the call
+ /**
+ * User didn't answer during call setup operation (MO/MT)
+ * MO : 200 OK to INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
public static final int CODE_TIMEOUT_NO_ANSWER = 202;
- // User no answer during call update operation (MO/MT)
- // MO : 200 OK to re-INVITE request is not received,
- // MT : No action from user after alerting the call
+ /**
+ * User no answer during call update operation (MO/MT)
+ * MO : 200 OK to re-INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203;
- //Call was blocked by call barring
+ /**
+ * The call was blocked by call barring configuration.
+ */
public static final int CODE_CALL_BARRED = 240;
- //Call failures for FDN
+ /**
+ * The operation is restricted to fixed dialing numbers only.
+ */
public static final int CODE_FDN_BLOCKED = 241;
- // Network does not accept the emergency call request because IMEI was used as identification
- // and this capability is not supported by the network.
+ /**
+ * Network rejected the emergency call request because IMEI was used as identification
+ * and this capability is not supported by the network.
+ */
public static final int CODE_IMEI_NOT_ACCEPTED = 243;
//STK CC errors
+ /**
+ * Stk Call Control modified DIAL request to USSD request.
+ */
public static final int CODE_DIAL_MODIFIED_TO_USSD = 244;
+ /**
+ * Stk Call Control modified DIAL request to SS request.
+ */
public static final int CODE_DIAL_MODIFIED_TO_SS = 245;
+ /**
+ * Stk Call Control modified DIAL request to DIAL with modified data.
+ */
public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246;
+ /**
+ * Stk Call Control modified DIAL request to Video DIAL request.
+ */
public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247;
+ /**
+ * Stk Call Control modified Video DIAL request to DIAL request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248;
+ /**
+ * Stk Call Control modified Video DIAL request to Video DIAL request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249;
+ /**
+ * Stk Call Control modified Video DIAL request to SS request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250;
+ /**
+ * Stk Call Control modified Video DIAL request to USSD request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251;
- /**
+ /*
* STATUSCODE (SIP response code) (IMS -> Telephony)
*/
// 3xx responses
- // SIP request is redirected
+ /**
+ * SIP 3xx response: SIP request is redirected
+ */
public static final int CODE_SIP_REDIRECTED = 321;
// 4xx responses
- // 400 : Bad Request
+ /**
+ * Sip 400 response : Bad Request
+ */
public static final int CODE_SIP_BAD_REQUEST = 331;
- // 403 : Forbidden
+ /**
+ * Sip 403 response : Forbidden
+ */
public static final int CODE_SIP_FORBIDDEN = 332;
- // 404 : Not Found
+ /**
+ * Sip 404 response : Not Found
+ */
public static final int CODE_SIP_NOT_FOUND = 333;
- // 415 : Unsupported Media Type
- // 416 : Unsupported URI Scheme
- // 420 : Bad Extension
+ /**
+ * Not supported, because of one of the following:
+ * SIP response 415 : Unsupported Media Type,
+ * SIP response 416 : Unsupported URI Scheme,
+ * SIP response 420 : Bad Extension
+ */
public static final int CODE_SIP_NOT_SUPPORTED = 334;
- // 408 : Request Timeout
+ /**
+ * SIP response 408 : Request Timeout.
+ */
public static final int CODE_SIP_REQUEST_TIMEOUT = 335;
- // 480 : Temporarily Unavailable
+ /**
+ * SIP response 480 : Temporarily Unavailable
+ */
public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336;
- // 484 : Address Incomplete
+ /**
+ * SIP response 484 : Address Incomplete
+ */
public static final int CODE_SIP_BAD_ADDRESS = 337;
- // 486 : Busy Here
- // 600 : Busy Everywhere
+ /**
+ * Returned a busy response, may be one of the following:
+ * SIP response 486 : Busy Here,
+ * SIP response 600 : Busy Everywhere
+ */
public static final int CODE_SIP_BUSY = 338;
- // 487 : Request Terminated
+ /**
+ * SIP response 487 : Request Terminated
+ */
public static final int CODE_SIP_REQUEST_CANCELLED = 339;
- // 406 : Not Acceptable
- // 488 : Not Acceptable Here
- // 606 : Not Acceptable
+ /**
+ * Received a not acceptable response, will be one of the following:
+ * SIP response 406 : Not Acceptable
+ * SIP response 488 : Not Acceptable Here
+ * SIP response 606 : Not Acceptable
+ */
public static final int CODE_SIP_NOT_ACCEPTABLE = 340;
- // 410 : Gone
- // 604 : Does Not Exist Anywhere
+ /**
+ * Received a not acceptable response, will be one of the following:
+ * SIP response 410 : Gone
+ * SIP response 604 : Does Not Exist Anywhere
+ */
public static final int CODE_SIP_NOT_REACHABLE = 341;
- // Others
+ /**
+ * Received another unspecified error SIP response from the client.
+ */
public static final int CODE_SIP_CLIENT_ERROR = 342;
- // 481 Transaction Does Not Exist
+ /**
+ * SIP response 481: Transaction Does Not Exist
+ */
public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343;
// 5xx responses
- // 501 : Server Internal Error
+ /**
+ * SIP response 501 : Server Internal Error
+ */
public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351;
- // 503 : Service Unavailable
+ /**
+ * SIP response 503 : Service Unavailable
+ */
public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352;
- // 504 : Server Time-out
+ /**
+ * SIP response 504 : Server Time-out
+ */
public static final int CODE_SIP_SERVER_TIMEOUT = 353;
- // Others
+ /**
+ * Received an unspecified SIP server error response.
+ */
public static final int CODE_SIP_SERVER_ERROR = 354;
// 6xx responses
- // 603 : Decline
+ /**
+ * 603 : Decline
+ */
public static final int CODE_SIP_USER_REJECTED = 361;
- // Others
+ /**
+ * Unspecified 6xx error.
+ */
public static final int CODE_SIP_GLOBAL_ERROR = 362;
- // Emergency failure
+
+ /**
+ * Emergency call failed in the modem with a temporary fail cause and should be redialed on this
+ * slot.
+ */
public static final int CODE_EMERGENCY_TEMP_FAILURE = 363;
+ /**
+ * Emergency call failed in the modem with a permanent fail cause and should not be redialed on
+ * this slot. If there are any other slots available for emergency calling, try those.
+ */
public static final int CODE_EMERGENCY_PERM_FAILURE = 364;
/**
@@ -193,82 +331,149 @@
*/
public static final int CODE_SIP_USER_MARKED_UNWANTED = 365;
- /**
+ /*
* MEDIA (IMS -> Telephony)
*/
- // Media resource initialization failed
+ /**
+ * Media resource initialization failed
+ */
public static final int CODE_MEDIA_INIT_FAILED = 401;
- // RTP timeout (no audio / video traffic in the session)
+ /**
+ * RTP timeout (no audio / video traffic in the session)
+ */
public static final int CODE_MEDIA_NO_DATA = 402;
- // Media is not supported; so dropped the call
+ /**
+ * Media is not supported; so dropped the call
+ */
public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403;
- // Unknown media related errors
+ /**
+ * Unspecified media related error.
+ */
public static final int CODE_MEDIA_UNSPECIFIED = 404;
- /**
+ /*
* USER
*/
// Telephony -> IMS
- // User triggers the call end
+ /**
+ * User triggers the call to be terminated.
+ */
public static final int CODE_USER_TERMINATED = 501;
- // No action while an incoming call is ringing
+ /**
+ * No action was taken while an incoming call was ringing.
+ */
public static final int CODE_USER_NOANSWER = 502;
- // User ignores an incoming call
+ /**
+ * User ignored an incoming call.
+ */
public static final int CODE_USER_IGNORE = 503;
- // User declines an incoming call
+ /**
+ * User declined an incoming call.
+ */
public static final int CODE_USER_DECLINE = 504;
- // Device declines/ends a call due to low battery
+ /**
+ * Device declined/ended a call due to a low battery condition.
+ */
public static final int CODE_LOW_BATTERY = 505;
- // Device declines call due to blacklisted call ID
+ /**
+ * Device declined a call due to a blacklisted caller ID.
+ */
public static final int CODE_BLACKLISTED_CALL_ID = 506;
// IMS -> Telephony
- // The call is terminated by the network or remote user
+ /**
+ * The call has been terminated by the network or remote user.
+ */
public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
- /**
- * Extra codes for the specific code value
- * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
- */
- // Try to connect CS call; normal
- public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
- // Try to connect CS call without the notification to user
- public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
- // Try to connect CS call by the settings of the menu
- public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
-
- /**
+ /*
* UT
*/
+ /**
+ * UT is currently not supported on this device.
+ */
public static final int CODE_UT_NOT_SUPPORTED = 801;
+ /**
+ * UT services are currently not available on this device.
+ */
public static final int CODE_UT_SERVICE_UNAVAILABLE = 802;
+ /**
+ * The requested UT operation is not allowed.
+ */
public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803;
+ /**
+ * The UT request resulted in a network error.
+ */
public static final int CODE_UT_NETWORK_ERROR = 804;
+ /**
+ * The password entered for UT operations does not match the stored password.
+ */
public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821;
//STK CC errors
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a dial command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a USSD command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to another supplementary service command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_SS = 824;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a video call dial command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825;
+ /**@hide*/
+ @IntDef(value = {
+ CODE_UT_NOT_SUPPORTED,
+ CODE_UT_SERVICE_UNAVAILABLE,
+ CODE_UT_OPERATION_NOT_ALLOWED,
+ CODE_UT_NETWORK_ERROR,
+ CODE_UT_CB_PASSWORD_MISMATCH,
+ CODE_UT_SS_MODIFIED_TO_DIAL,
+ CODE_UT_SS_MODIFIED_TO_USSD,
+ CODE_UT_SS_MODIFIED_TO_SS,
+ CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO
+ }, prefix = "CODE_UT_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UtReason {}
+
/**
- * ECBM
+ * Emergency callback mode is not supported.
*/
public static final int CODE_ECBM_NOT_SUPPORTED = 901;
/**
- * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+ * Fail code used to indicate that Multi-endpoint is not supported by the IMS framework.
*/
public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902;
/**
- * Ims Registration error code
+ * IMS Registration error code
*/
public static final int CODE_REGISTRATION_ERROR = 1000;
- /**
+ /*
* CALL DROP error codes (Call could drop because of many reasons like Network not available,
* handover, failed, etc)
*/
+ /**
+ * MT call has ended due to a release from the network because the call was answered elsewhere.
+ */
+ public static final int CODE_ANSWERED_ELSEWHERE = 1014;
+
+ /**
+ * For MultiEndpoint - Call Pull request has failed.
+ */
+ public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
+
+ /**
+ * For MultiEndpoint - Call has been pulled from primary to secondary.
+ */
+ public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
/**
* CALL DROP error code for the case when a device is ePDG capable and when the user is on an
@@ -279,46 +484,35 @@
public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
/**
- * MT call has ended due to a release from the network
- * because the call was answered elsewhere
- */
- public static final int CODE_ANSWERED_ELSEWHERE = 1014;
-
- /**
- * For MultiEndpoint - Call Pull request has failed
- */
- public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
-
- /**
- * For MultiEndpoint - Call has been pulled from primary to secondary
- */
- public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
-
- /**
- * Supplementary services (HOLD/RESUME) failure error codes.
- * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+ * Supplementary Services (HOLD/RESUME) - the command failed.
*/
public static final int CODE_SUPP_SVC_FAILED = 1201;
+ /**
+ * Supplementary Services (HOLD/RESUME) - the command was cancelled.
+ */
public static final int CODE_SUPP_SVC_CANCELLED = 1202;
+ /**
+ * Supplementary Services (HOLD/RESUME) - the command resulted in a re-invite collision.
+ */
public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203;
/**
- * DPD Procedure received no response or send failed
+ * DPD Procedure received no response or send failed.
*/
public static final int CODE_IWLAN_DPD_FAILURE = 1300;
/**
- * Establishment of the ePDG Tunnel Failed
+ * Establishment of the ePDG Tunnel Failed.
*/
public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400;
/**
- * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+ * Re-keying of the ePDG Tunnel Failed; may not always result in teardown.
*/
public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401;
/**
- * Connection to the packet gateway is lost
+ * Connection to the packet gateway is lost.
*/
public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402;
@@ -571,8 +765,10 @@
*/
public static final int CODE_REJECT_ONGOING_CS_CALL = 1621;
- /* OEM specific error codes. To be used by OEMs when they don't want to
- reveal error code which would be replaced by ERROR_UNSPECIFIED */
+ /*
+ * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
+ * would be replaced by ERROR_UNSPECIFIED.
+ */
public static final int CODE_OEM_CAUSE_1 = 0xf001;
public static final int CODE_OEM_CAUSE_2 = 0xf002;
public static final int CODE_OEM_CAUSE_3 = 0xf003;
@@ -597,6 +793,33 @@
= "Forbidden. Not Authorized for Service";
+ /*
+ * Extra codes for the specific code value
+ * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
+ */
+ /**
+ * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+ * been returned.
+ * <p>
+ * Try to connect the call using CS
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
+ /**
+ * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+ * been returned.
+ * <p>
+ * Try to connect the call using CS and do not notify the user.
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
+ /**
+ * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+ * been returned.
+ * <p>
+ * Try to connect the call using CS by using the settings.
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
+
+
// For main reason code
/** @hide */
@UnsupportedAppUsage
@@ -638,29 +861,28 @@
}
/**
- *
+ * @return an integer representing more information about the completion of an operation.
*/
public int getCode() {
return mCode;
}
/**
- *
+ * @return an optional OEM specified code that provides extra information.
*/
public int getExtraCode() {
return mExtraCode;
}
/**
- *
+ * @return an optional OEM specified string that provides extra information about the operation
+ * result.
*/
public String getExtraMessage() {
return mExtraMessage;
}
/**
- * Returns the string format of {@link ImsReasonInfo}
- *
* @return the string format of {@link ImsReasonInfo}
*/
public String toString() {
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index db5ba47..3a82517 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -65,6 +65,17 @@
public static final int SS_INCOMING_BARRING_DN = 21;
public static final int SS_INCOMING_BARRING_ANONYMOUS = 22;
+
+ /**@hide*/
+ @IntDef(flag = true, prefix = {"SS_"}, value = {
+ SS_ACTIVATION,
+ SS_DEACTIVATION,
+ SS_INTERROGATION,
+ SS_REGISTRATION,
+ SS_ERASURE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestType{}
+
//Supplementary Service Request Types
public static final int SS_ACTIVATION = 0;
public static final int SS_DEACTIVATION = 1;
@@ -72,6 +83,17 @@
public static final int SS_REGISTRATION = 3;
public static final int SS_ERASURE = 4;
+ /**@hide*/
+ @IntDef(flag = true, prefix = {"SS_"}, value = {
+ SS_ALL_TELE_AND_BEARER_SERVICES,
+ SS_ALL_TELESEVICES,
+ SS_TELEPHONY,
+ SS_ALL_DATA_TELESERVICES,
+ SS_SMS_SERVICES,
+ SS_ALL_TELESERVICES_EXCEPT_SMS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TeleserviceType{}
+
// Supplementary Service Teleservice Type
public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0;
public static final int SS_ALL_TELESEVICES = 1;
@@ -191,21 +213,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceType{}
- /** @hide */
- @IntDef(flag = true, prefix = { "SERVICE_CLASS" }, value = {
- SERVICE_CLASS_NONE,
- SERVICE_CLASS_VOICE,
- SERVICE_CLASS_DATA,
- SERVICE_CLASS_FAX,
- SERVICE_CLASS_SMS,
- SERVICE_CLASS_DATA_CIRCUIT_SYNC,
- SERVICE_CLASS_DATA_CIRCUIT_ASYNC,
- SERVICE_CLASS_DATA_PACKET_ACCESS,
- SERVICE_CLASS_DATA_PAD
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ServiceClass{}
-
/**
* The Service type of this Supplementary service.
* @hide
@@ -221,7 +228,7 @@
* {@link #SS_ERASURE}
* @hide
*/
- public final int requestType;
+ public final @RequestType int requestType;
/**
* Supplementary Service teleservice type:
@@ -234,14 +241,14 @@
*
* @hide
*/
- public final int teleserviceType;
+ public final @TeleserviceType int teleserviceType;
/**
* Supplementary Service service class.
*
* @hide
*/
- public final @ServiceClass int serviceClass;
+ public final @ServiceClassFlags int serviceClass;
/**
* Result of Supplementary Service operation. Valid values are:
@@ -285,7 +292,7 @@
* @see #build()
*/
public Builder(@ServiceType int serviceType, int requestType, int teleserviceType,
- @ServiceClass int serviceClass, int result) {
+ @ServiceClassFlags int serviceClass, int result) {
mImsSsData = new ImsSsData(serviceType, requestType, teleserviceType, serviceClass,
result);
}
@@ -294,7 +301,7 @@
* Set the array of {@link ImsSsInfo}s that are associated with this supplementary service
* data.
*/
- public Builder setSuppServiceInfo(@NonNull ImsSsInfo[] imsSsInfos) {
+ public @NonNull Builder setSuppServiceInfo(@NonNull ImsSsInfo[] imsSsInfos) {
mImsSsData.mImsSsInfo = imsSsInfos;
return this;
}
@@ -303,7 +310,8 @@
* Set the array of {@link ImsCallForwardInfo}s that are associated with this supplementary
* service data.
*/
- public Builder setCallForwardingInfo(@NonNull ImsCallForwardInfo[] imsCallForwardInfos) {
+ public @NonNull Builder setCallForwardingInfo(
+ @NonNull ImsCallForwardInfo[] imsCallForwardInfos) {
mImsSsData.mCfInfo = imsCallForwardInfos;
return this;
}
@@ -311,7 +319,7 @@
/**
* @return an {@link ImsSsData} containing optional parameters.
*/
- public ImsSsData build() {
+ public @NonNull ImsSsData build() {
return mImsSsData;
}
}
@@ -337,7 +345,7 @@
* success, or ImsReasonInfo code if the result is a failure.
*/
public ImsSsData(@ServiceType int serviceType, int requestType, int teleserviceType,
- @ServiceClass int serviceClass, int result) {
+ @ServiceClassFlags int serviceClass, int result) {
this.serviceType = serviceType;
this.requestType = requestType;
this.teleserviceType = teleserviceType;
@@ -449,14 +457,9 @@
}
/**
- * Supplementary Service request Type:
- * {@link #SS_ACTIVATION),
- * {@link #SS_DEACTIVATION},
- * {@link #SS_INTERROGATION},
- * {@link #SS_REGISTRATION},
- * {@link #SS_ERASURE}
+ * Supplementary Service request Type.
*/
- public int getRequestType() {
+ public @RequestType int getRequestType() {
return requestType;
}
@@ -468,31 +471,25 @@
}
/**
- * Supplementary Service teleservice type:
- * {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
- * {@link #SS_ALL_TELESEVICES},
- * {@link #SS_TELEPHONY},
- * {@link #SS_ALL_DATA_TELESERVICES},
- * {@link #SS_SMS_SERVICES},
- * {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+ * Supplementary Service teleservice type.
*/
- public int getTeleserviceType() {
+ public @TeleserviceType int getTeleserviceType() {
return teleserviceType;
}
/**
* Supplementary Service service class.
*/
- public @ServiceClass int getServiceClass() {
+ public @ServiceClassFlags int getServiceClass() {
return serviceClass;
}
/**
* Result of Supplementary Service operation. Valid values are:
* {@link #RESULT_SUCCESS} if the result is success, or
- * {@link ImsReasonInfo} CODE_* code if the result is a failure.
+ * {@link ImsReasonInfo.UtReason} code if the result is a failure.
*/
- public int getResult() {
+ public @ImsReasonInfo.UtReason int getResult() {
return result;
}
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 0af6e62..031f9e1 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -82,6 +82,7 @@
*/
public static final int SERVICE_PROVISIONED = 1;
+ /**@hide*/
@IntDef(value = {
CLIR_OUTGOING_DEFAULT,
CLIR_OUTGOING_INVOCATION,
@@ -141,6 +142,7 @@
*/
public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4;
+ /**@hide*/
@IntDef(value = {
CLIR_STATUS_NOT_PROVISIONED,
CLIR_STATUS_PROVISIONED_PERMANENT,
@@ -184,7 +186,7 @@
* Set the ICB number for IMS call barring.
* @param number The number in E.164 international format.
*/
- public Builder setIncomingCommunicationBarringNumber(@NonNull String number) {
+ public @NonNull Builder setIncomingCommunicationBarringNumber(@NonNull String number) {
mImsSsInfo.mIcbNum = number;
return this;
}
@@ -192,7 +194,7 @@
/**
* Set the provisioning status for a Supplementary Service interrogation response.
*/
- public Builder setProvisionStatus(@ServiceProvisionStatus int provisionStatus) {
+ public @NonNull Builder setProvisionStatus(@ServiceProvisionStatus int provisionStatus) {
mImsSsInfo.mProvisionStatus = provisionStatus;
return this;
}
@@ -201,7 +203,7 @@
* Set the Calling Line Identification Restriction (CLIR) status for a supplementary service
* interrogation response.
*/
- public Builder setClirInterrogationStatus(@ClirInterrogationStatus int status) {
+ public @NonNull Builder setClirInterrogationStatus(@ClirInterrogationStatus int status) {
mImsSsInfo.mClirInterrogationStatus = status;
return this;
}
@@ -209,7 +211,7 @@
/**
* Set the Calling line identification Restriction (CLIR) state for outgoing calls.
*/
- public Builder setClirOutgoingState(@ClirOutgoingState int state) {
+ public @NonNull Builder setClirOutgoingState(@ClirOutgoingState int state) {
mImsSsInfo.mClirOutgoingState = state;
return this;
}
@@ -217,7 +219,7 @@
/**
* @return a built {@link ImsSsInfo} containing optional the parameters that were set.
*/
- public ImsSsInfo build() {
+ public @NonNull ImsSsInfo build() {
return mImsSsInfo;
}
}
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 0abe45c..13539b8 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
@@ -32,15 +33,15 @@
import android.provider.ContactsContract.RawContacts;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
-import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import com.android.i18n.phonenumbers.NumberParseException;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
-import android.telephony.SubscriptionManager;
+import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import java.util.Locale;
@@ -112,6 +113,9 @@
public Uri contactRefUri;
public String lookupKey;
+ public ComponentName preferredPhoneAccountComponent;
+ public String preferredPhoneAccountId;
+
public long userType;
/**
@@ -264,6 +268,17 @@
info.contactDisplayPhotoUri = null;
}
+ columnIndex = cursor.getColumnIndex(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME);
+ if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
+ info.preferredPhoneAccountComponent =
+ ComponentName.unflattenFromString(cursor.getString(columnIndex));
+ }
+
+ columnIndex = cursor.getColumnIndex(Data.PREFERRED_PHONE_ACCOUNT_ID);
+ if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
+ info.preferredPhoneAccountId = cursor.getString(columnIndex);
+ }
+
// look for the custom ringtone, create from the string stored
// in the database.
// An empty string ("") in the database indicates a silent ringtone,
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index f9db4b0..65d1a920 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -210,6 +210,10 @@
*/
List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage);
+ boolean removeSubscriptionsFromGroup(in int[] subIdList, String callingPackage);
+
+ List<SubscriptionInfo> getSubscriptionsInGroup(int subId, String callingPackage);
+
int getSlotIndex(int subId);
int[] getSubId(int slotIndex);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 88b9302..b12e7cc 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1337,18 +1337,6 @@
String getSubscriptionCarrierName(int subId);
/**
- * Returns MNO carrier id of the current subscription’s MCCMNC.
- * <p>MNO carrier id can be solely identified by subscription mccmnc. This is mainly used
- * for MNO fallback when exact carrier id {@link #getSimCarrierId()}
- * configurations are not found.
- *
- * @return MNO carrier id of the current subscription. Return the value same as carrier id
- * {@link #getSimCarrierId()}, if MNO carrier id cannot be identified.
- * @hide
- */
- int getSubscriptionMNOCarrierId(int subId);
-
- /**
* Returns fine-grained carrier id of the current subscription.
*
* <p>The precise carrier id can be used to further differentiate a carrier by different
@@ -1383,10 +1371,13 @@
* 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
*
+ * @param isSubscriptionMccMnc. If {@true} it means this is a query for subscription mccmnc
+ * {@false} otherwise.
+ *
* @return carrier id from passing mccmnc.
* @hide
*/
- int getCarrierIdFromMccMnc(int slotIndex, String mccmnc);
+ int getCarrierIdFromMccMnc(int slotIndex, String mccmnc, boolean isSubscriptionMccMnc);
/**
* Action set from carrier signalling broadcast receivers to enable/disable metered apns
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index be74a6d..7a5e732 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -92,11 +92,11 @@
event.mPackage = "fake.package.name" + pkg;
event.mClass = event.mPackage + ".class1";
event.mTimeStamp = 1;
- event.mEventType = UsageEvents.Event.MOVE_TO_FOREGROUND;
+ event.mEventType = UsageEvents.Event.ACTIVITY_RESUMED;
for (int evt = 0; evt < eventsPerPackage; evt++) {
intervalStats.events.insert(event);
intervalStats.update(event.mPackage, event.mClass, event.mTimeStamp,
- event.mEventType);
+ event.mEventType, 1);
}
}
}
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
index 3480e96..53afa26 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
@@ -21,13 +21,14 @@
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
-import androidx.collection.CircularArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
+import androidx.collection.CircularArray;
+
public class UsageLogActivity extends ListActivity implements Runnable {
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
@@ -155,10 +156,10 @@
private String eventToString(int eventType) {
switch (eventType) {
- case UsageEvents.Event.MOVE_TO_FOREGROUND:
+ case UsageEvents.Event.ACTIVITY_RESUMED:
return "Foreground";
- case UsageEvents.Event.MOVE_TO_BACKGROUND:
+ case UsageEvents.Event.ACTIVITY_PAUSED:
return "Background";
case UsageEvents.Event.CONFIGURATION_CHANGE:
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 50aef1d..84f7359 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -474,7 +474,7 @@
new StringNetworkSpecifier("specs"));
try {
nc2.addTransportType(TRANSPORT_WIFI);
- fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!");
+ fail("Cannot set a second TransportType of a network which has a NetworkSpecifier!");
} catch (IllegalStateException expected) {
// empty
}
@@ -500,16 +500,23 @@
// empty
});
NetworkCapabilities nc2 = new NetworkCapabilities();
+ // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where
+ // combine fails)
nc2.setTransportInfo(new TransportInfo() {
// empty
});
try {
nc1.combineCapabilities(nc2);
- fail("Should not be able to combine NetworkCaabilities which contain TransportInfos");
+ fail("Should not be able to combine NetworkCabilities which contain TransportInfos");
} catch (IllegalStateException expected) {
// empty
}
+
+ // verify that can combine with identical TransportInfo objects
+ NetworkCapabilities nc3 = new NetworkCapabilities();
+ nc3.setTransportInfo(nc1.getTransportInfo());
+ nc1.combineCapabilities(nc3);
}
private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) {
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 583f14a..9460c9e 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -306,31 +306,6 @@
break;
}
- for (size_t i = 0; i < entry->overlayable_declarations.size(); i++) {
- printer->Print((i == 0) ? " " : "|");
- printer->Print("OVERLAYABLE");
-
- if (entry->overlayable_declarations[i].policy) {
- switch (entry->overlayable_declarations[i].policy.value()) {
- case Overlayable::Policy::kProduct:
- printer->Print("_PRODUCT");
- break;
- case Overlayable::Policy::kProductServices:
- printer->Print("_PRODUCT_SERVICES");
- break;
- case Overlayable::Policy::kSystem:
- printer->Print("_SYSTEM");
- break;
- case Overlayable::Policy::kVendor:
- printer->Print("_VENDOR");
- break;
- case Overlayable::Policy::kPublic:
- printer->Print("_PUBLIC");
- break;
- }
- }
- }
-
printer->Println();
if (options.show_values) {
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 4f25e09..9587704 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -99,7 +99,7 @@
ResourceId id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
bool allow_new = false;
- std::vector<Overlayable> overlayable_declarations;
+ Maybe<Overlayable> overlayable;
std::string comment;
std::unique_ptr<Value> value;
@@ -133,8 +133,8 @@
}
}
- for (auto& overlayable : res->overlayable_declarations) {
- if (!table->AddOverlayable(res->name, overlayable, diag)) {
+ if (res->overlayable) {
+ if (!table->SetOverlayable(res->name, res->overlayable.value(), diag)) {
return false;
}
}
@@ -1063,20 +1063,19 @@
<< "' for <overlayable> tag");
}
- std::string comment;
- std::vector<Overlayable::Policy> policies;
-
bool error = false;
+ std::string comment;
+ Overlayable::PolicyFlags current_policies = Overlayable::Policy::kNone;
const size_t start_depth = parser->depth();
while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
xml::XmlPullParser::Event event = parser->event();
if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
- // Break the loop when exiting the overyabale element
+ // Break the loop when exiting the overlayable element
break;
} else if (event == xml::XmlPullParser::Event::kEndElement
&& parser->depth() == start_depth + 1) {
// Clear the current policies when exiting the policy element
- policies.clear();
+ current_policies = Overlayable::Policy::kNone;
continue;
} else if (event == xml::XmlPullParser::Event::kComment) {
// Get the comment of individual item elements
@@ -1090,43 +1089,71 @@
const Source item_source = source_.WithLine(parser->line_number());
const std::string& element_name = parser->element_name();
const std::string& element_namespace = parser->element_namespace();
-
if (element_namespace.empty() && element_name == "item") {
- if (!ParseOverlayableItem(parser, policies, comment, out_resource)) {
+ // Items specify the name and type of resource that should be overlayable
+ Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(item_source)
+ << "<item> within an <overlayable> tag must have a 'name' attribute");
error = true;
+ continue;
}
+
+ Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ if (!maybe_type) {
+ diag_->Error(DiagMessage(item_source)
+ << "<item> within an <overlayable> tag must have a 'type' attribute");
+ error = true;
+ continue;
+ }
+
+ const ResourceType* type = ParseResourceType(maybe_type.value());
+ if (type == nullptr) {
+ diag_->Error(DiagMessage(item_source)
+ << "invalid resource type '" << maybe_type.value()
+ << "' in <item> within an <overlayable>");
+ error = true;
+ continue;
+ }
+
+ ParsedResource child_resource;
+ child_resource.name.type = *type;
+ child_resource.name.entry = maybe_name.value().to_string();
+ child_resource.overlayable = Overlayable{current_policies, item_source, comment};
+ out_resource->child_resources.push_back(std::move(child_resource));
+
} else if (element_namespace.empty() && element_name == "policy") {
- if (!policies.empty()) {
+ if (current_policies != Overlayable::Policy::kNone) {
// If the policy list is not empty, then we are currently inside a policy element
diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
error = true;
break;
} else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
// Parse the polices separated by vertical bar characters to allow for specifying multiple
- // policies at once
+ // policies
for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
StringPiece trimmed_part = util::TrimWhitespace(part);
if (trimmed_part == "public") {
- policies.push_back(Overlayable::Policy::kPublic);
+ current_policies |= Overlayable::Policy::kPublic;
} else if (trimmed_part == "product") {
- policies.push_back(Overlayable::Policy::kProduct);
+ current_policies |= Overlayable::Policy::kProduct;
} else if (trimmed_part == "product_services") {
- policies.push_back(Overlayable::Policy::kProductServices);
+ current_policies |= Overlayable::Policy::kProductServices;
} else if (trimmed_part == "system") {
- policies.push_back(Overlayable::Policy::kSystem);
+ current_policies |= Overlayable::Policy::kSystem;
} else if (trimmed_part == "vendor") {
- policies.push_back(Overlayable::Policy::kVendor);
+ current_policies |= Overlayable::Policy::kVendor;
} else {
- diag_->Error(DiagMessage(out_resource->source)
- << "<policy> has unsupported type '" << trimmed_part << "'");
+ diag_->Error(DiagMessage(item_source)
+ << "<policy> has unsupported type '" << trimmed_part << "'");
error = true;
continue;
}
}
}
} else if (!ShouldIgnoreElement(element_namespace, element_name)) {
- diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> in "
- << " <overlayable>");
+ diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> "
+ << " in <overlayable>");
error = true;
break;
}
@@ -1135,61 +1162,6 @@
return !error;
}
-bool ResourceParser::ParseOverlayableItem(xml::XmlPullParser* parser,
- const std::vector<Overlayable::Policy>& policies,
- const std::string& comment,
- ParsedResource* out_resource) {
- const Source item_source = source_.WithLine(parser->line_number());
-
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
- if (!maybe_name) {
- diag_->Error(DiagMessage(item_source)
- << "<item> within an <overlayable> tag must have a 'name' attribute");
- return false;
- }
-
- Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
- if (!maybe_type) {
- diag_->Error(DiagMessage(item_source)
- << "<item> within an <overlayable> tag must have a 'type' attribute");
- return false;
- }
-
- const ResourceType* type = ParseResourceType(maybe_type.value());
- if (type == nullptr) {
- diag_->Error(DiagMessage(out_resource->source)
- << "invalid resource type '" << maybe_type.value()
- << "' in <item> within an <overlayable>");
- return false;
- }
-
- ParsedResource child_resource;
- child_resource.name.type = *type;
- child_resource.name.entry = maybe_name.value().to_string();
- child_resource.source = item_source;
-
- if (policies.empty()) {
- Overlayable overlayable;
- overlayable.source = item_source;
- overlayable.comment = comment;
- child_resource.overlayable_declarations.push_back(overlayable);
- } else {
- for (Overlayable::Policy policy : policies) {
- Overlayable overlayable;
- overlayable.policy = policy;
- overlayable.source = item_source;
- overlayable.comment = comment;
- child_resource.overlayable_declarations.push_back(overlayable);
- }
- }
-
- if (options_.visibility) {
- child_resource.visibility_level = options_.visibility.value();
- }
- out_resource->child_resources.push_back(std::move(child_resource));
- return true;
-}
-
bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (ParseSymbolImpl(parser, out_resource)) {
out_resource->visibility_level = Visibility::Level::kUndefined;
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index ebacd6f..06bb0c9 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -96,10 +96,6 @@
bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
- bool ParseOverlayableItem(xml::XmlPullParser* parser,
- const std::vector<Overlayable::Policy>& policies,
- const std::string& comment,
- ParsedResource* out_resource);
bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index c6f29ac..03e6197 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -905,16 +905,16 @@
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
+ Eq(Overlayable::Policy::kNone));
search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
+ Eq(Overlayable::Policy::kNone));
}
TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
@@ -945,49 +945,44 @@
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kNone));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProduct));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct));
search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProductServices));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProductServices));
search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kSystem));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kSystem));
search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kVendor));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor));
search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kPublic));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kPublic));
}
TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
@@ -1031,22 +1026,18 @@
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kVendor));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kProductServices));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor
+ | Overlayable::Policy::kProductServices));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProduct));
- EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kSystem));
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct
+ | Overlayable::Policy::kSystem));
}
TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
@@ -1067,7 +1058,7 @@
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable">
+ <overlayable>
<policy type="product">
<item type="string" name="foo" />
<item type="string" name="foo" />
@@ -1080,6 +1071,26 @@
<policy type="product">
<item type="string" name="foo" />
</policy>
+ <item type="string" name="foo" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable>
+ <policy type="product">
+ <item type="string" name="foo" />
+ </policy>
+ <policy type="vendor">
+ <item type="string" name="foo" />
+ </policy>
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable>
+ <policy type="product">
+ <item type="string" name="foo" />
+ </policy>
</overlayable>
<overlayable>
@@ -1090,41 +1101,6 @@
EXPECT_FALSE(TestParse(input));
}
-TEST_F(ResourceParserTest, PolicyAndNonPolicyOverlayableError) {
- std::string input = R"(
- <overlayable policy="product">
- <item type="string" name="foo" />
- </overlayable>
- <overlayable policy="">
- <item type="string" name="foo" />
- </overlayable>)";
- EXPECT_FALSE(TestParse(input));
-
- input = R"(
- <overlayable policy="">
- <item type="string" name="foo" />
- </overlayable>
- <overlayable policy="product">
- <item type="string" name="foo" />
- </overlayable>)";
- EXPECT_FALSE(TestParse(input));
-}
-
-TEST_F(ResourceParserTest, DuplicateOverlayableMultiplePolicyError) {
- std::string input = R"(
- <overlayable>
- <policy type="vendor|product">
- <item type="string" name="foo" />
- </policy>
- </overlayable>
- <overlayable>
- <policy type="product_services|vendor">
- <item type="string" name="foo" />
- </policy>
- </overlayable>)";
- EXPECT_FALSE(TestParse(input));
-}
-
TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
std::string input = R"(
<overlayable>
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index bc8a4d1..54633ad 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -625,18 +625,18 @@
return true;
}
-bool ResourceTable::AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
IDiagnostics* diag) {
- return AddOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
+ return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
}
-bool ResourceTable::AddOverlayableMangled(const ResourceNameRef& name,
+bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
const Overlayable& overlayable, IDiagnostics* diag) {
- return AddOverlayableImpl(name, overlayable, SkipNameValidator, diag);
+ return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
}
-bool ResourceTable::AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
- NameValidator name_validator, IDiagnostics* diag) {
+bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+ NameValidator name_validator, IDiagnostics *diag) {
CHECK(diag != nullptr);
if (!ValidateName(name_validator, name, overlayable.source, diag)) {
@@ -647,27 +647,14 @@
ResourceTableType* type = package->FindOrCreateType(name.type);
ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
- for (auto& overlayable_declaration : entry->overlayable_declarations) {
- // An overlayable resource cannot be declared twice with the same policy
- if (overlayable.policy == overlayable_declaration.policy) {
- diag->Error(DiagMessage(overlayable.source)
+ if (entry->overlayable) {
+ diag->Error(DiagMessage(overlayable.source)
<< "duplicate overlayable declaration for resource '" << name << "'");
- diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here");
- return false;
- }
-
- // An overlayable resource cannot be declared once with a policy and without a policy because
- // the policy becomes unused
- if (!overlayable.policy || !overlayable_declaration.policy) {
- diag->Error(DiagMessage(overlayable.source)
- << "overlayable resource '" << name << "'"
- << " declared once with a policy and once with no policy");
- diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here");
- return false;
- }
+ diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
+ return false;
}
- entry->overlayable_declarations.push_back(overlayable);
+ entry->overlayable = overlayable;
return true;
}
@@ -703,7 +690,7 @@
new_entry->id = entry->id;
new_entry->visibility = entry->visibility;
new_entry->allow_new = entry->allow_new;
- new_entry->overlayable_declarations = entry->overlayable_declarations;
+ new_entry->overlayable = entry->overlayable;
for (const auto& config_value : entry->values) {
ResourceConfigValue* new_value =
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 3dd0a769..e646f5b 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -57,27 +57,32 @@
std::string comment;
};
-// Represents a declaration that a resource is overayable at runtime.
+// Represents a declaration that a resource is overlayable at runtime.
struct Overlayable {
+
// Represents the types overlays that are allowed to overlay the resource.
- enum class Policy {
+ enum Policy : uint32_t {
+ kNone = 0x00,
+
// The resource can be overlaid by any overlay.
- kPublic,
+ kPublic = 0x01,
// The resource can be overlaid by any overlay on the system partition.
- kSystem,
+ kSystem = 0x02,
// The resource can be overlaid by any overlay on the vendor partition.
- kVendor,
+ kVendor = 0x04,
// The resource can be overlaid by any overlay on the product partition.
- kProduct,
+ kProduct = 0x08,
// The resource can be overlaid by any overlay on the product services partition.
- kProductServices,
+ kProductServices = 0x10
};
- Maybe<Policy> policy;
+ typedef uint32_t PolicyFlags;
+ PolicyFlags policies = Policy::kNone;
+
Source source;
std::string comment;
};
@@ -116,7 +121,7 @@
Maybe<AllowNew> allow_new;
// The declarations of this resource as overlayable for RROs
- std::vector<Overlayable> overlayable_declarations;
+ Maybe<Overlayable> overlayable;
// The resource's values for each configuration.
std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -246,9 +251,9 @@
bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
const ResourceId& res_id, IDiagnostics* diag);
- bool AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
- IDiagnostics* diag);
- bool AddOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
+ bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+ IDiagnostics *diag);
+ bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
IDiagnostics* diag);
bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
@@ -323,8 +328,8 @@
bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
NameValidator name_validator, IDiagnostics* diag);
- bool AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
- NameValidator name_validator, IDiagnostics* diag);
+ bool SetOverlayableImpl(const ResourceNameRef &name, const Overlayable &overlayable,
+ NameValidator name_validator, IDiagnostics *diag);
bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
const Visibility& symbol, NameValidator name_validator,
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 7c28f07..31095c4 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -242,69 +242,50 @@
ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("second"));
}
-TEST(ResourceTableTest, AddOverlayable) {
+TEST(ResourceTableTest, SetOverlayable) {
ResourceTable table;
+ Overlayable overlayable{};
+ overlayable.policies |= Overlayable::Policy::kProduct;
+ overlayable.policies |= Overlayable::Policy::kProductServices;
+ overlayable.comment = "comment";
+
const ResourceName name = test::ParseNameOrDie("android:string/foo");
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+ Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
- Overlayable overlayable;
- overlayable.policy = Overlayable::Policy::kProduct;
- overlayable.comment = "first";
- ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
- Maybe<ResourceTable::SearchResult> result = table.FindResource(name);
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
- ASSERT_THAT(result.value().entry->overlayable_declarations[0].comment, StrEq("first"));
- ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProduct));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable overlayable2;
- overlayable2.comment = "second";
- overlayable2.policy = Overlayable::Policy::kProductServices;
- ASSERT_TRUE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
- result = table.FindResource(name);
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
- ASSERT_THAT(result.value().entry->overlayable_declarations[0].comment, StrEq("first"));
- ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProduct));
- ASSERT_THAT(result.value().entry->overlayable_declarations[1].comment, StrEq("second"));
- ASSERT_THAT(result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kProductServices));
+ Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
+ ASSERT_THAT(result_overlayable.comment, StrEq("comment"));
+ EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
+ | Overlayable::Policy::kProductServices));
}
-TEST(ResourceTableTest, AddDuplicateOverlayableFail) {
+TEST(ResourceTableTest, AddDuplicateOverlayableSamePolicyFail) {
ResourceTable table;
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- Overlayable overlayable;
- overlayable.policy = Overlayable::Policy::kProduct;
- ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
+ Overlayable overlayable{};
+ overlayable.policies = Overlayable::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
- Overlayable overlayable2;
- overlayable2.policy = Overlayable::Policy::kProduct;
- ASSERT_FALSE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
+ Overlayable overlayable2{};
+ overlayable2.policies = Overlayable::Policy::kProduct;
+ ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
}
-TEST(ResourceTableTest, AddOverlayablePolicyAndNoneFirstFail) {
+TEST(ResourceTableTest, AddDuplicateOverlayableDifferentPolicyFail) {
ResourceTable table;
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- ASSERT_TRUE(table.AddOverlayable(name, {}, test::GetDiagnostics()));
+ Overlayable overlayable{};
+ overlayable.policies = Overlayable::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
- Overlayable overlayable2;
- overlayable2.policy = Overlayable::Policy::kProduct;
- ASSERT_FALSE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
-}
-
-TEST(ResourceTableTest, AddOverlayablePolicyAndNoneLastFail) {
- ResourceTable table;
- const ResourceName name = test::ParseNameOrDie("android:string/foo");
-
- Overlayable overlayable;
- overlayable.policy = Overlayable::Policy::kProduct;
- ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
-
- ASSERT_FALSE(table.AddOverlayable(name, {}, test::GetDiagnostics()));
+ Overlayable overlayable2{};
+ overlayable2.policies = Overlayable::Policy::kVendor;
+ ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
}
TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index bf9fe49..81a2c2e 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -136,12 +136,11 @@
// Represents a declaration that a resource is overayable at runtime.
message Overlayable {
enum Policy {
- NONE = 0;
- PUBLIC = 1;
- SYSTEM = 2;
- VENDOR = 3;
- PRODUCT = 4;
- PRODUCT_SERVICES = 5;
+ PUBLIC = 0;
+ SYSTEM = 1;
+ VENDOR = 2;
+ PRODUCT = 3;
+ PRODUCT_SERVICES = 4;
}
// Where this declaration was defined in source.
@@ -150,8 +149,8 @@
// Any comment associated with the declaration.
string comment = 2;
- // The policy of the overlayable declaration
- Policy policy = 3;
+ // The policy defined in the overlayable declaration.
+ repeated Policy policy = 3;
}
// An entry ID in the range [0x0000, 0xffff].
@@ -181,7 +180,7 @@
AllowNew allow_new = 4;
// Whether this resource can be overlaid by a runtime resource overlay (RRO).
- repeated Overlayable overlayable = 5;
+ Overlayable overlayable = 5;
// The set of values defined for this entry, each corresponding to a different
// configuration/variant.
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index 89d19cf..5cf056e 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -255,6 +255,7 @@
AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlTreeCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpBadgerCommand>(printer), /* hidden */ true);
+ // TODO(b/120609160): Add aapt2 overlayable dump command
}
int Action(const std::vector<std::string>& args) override {
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 3a71e83..2ef8b99 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -230,7 +230,7 @@
test::ParseConfigOrDie("fr"), test::ParseConfigOrDie("de")));
ASSERT_TRUE(a1.android_sdk);
ASSERT_TRUE(a1.android_sdk.value().min_sdk_version);
- EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19l);
+ EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19L);
EXPECT_THAT(a1.textures, SizeIs(1ul));
EXPECT_THAT(a1.features, SizeIs(1ul));
@@ -250,7 +250,7 @@
test::ParseConfigOrDie("fr-rCA")));
ASSERT_TRUE(a2.android_sdk);
ASSERT_TRUE(a2.android_sdk.value().min_sdk_version);
- EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19l);
+ EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19L);
EXPECT_THAT(a2.textures, SizeIs(1ul));
EXPECT_THAT(a2.features, SizeIs(1ul));
}
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index df0daeb..61ebd4e 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -441,25 +441,25 @@
const ResTable_overlayable_policy_header* policy_header =
ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
- std::vector<Overlayable::Policy> policies;
+ Overlayable::PolicyFlags policies = Overlayable::Policy::kNone;
if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
- policies.push_back(Overlayable::Policy::kPublic);
+ policies |= Overlayable::Policy::kPublic;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
- policies.push_back(Overlayable::Policy::kSystem);
+ policies |= Overlayable::Policy::kSystem;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
- policies.push_back(Overlayable::Policy::kVendor);
+ policies |= Overlayable::Policy::kVendor;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
- policies.push_back(Overlayable::Policy::kProduct);
+ policies |= Overlayable::Policy::kProduct;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
- policies.push_back(Overlayable::Policy::kProductServices);
+ policies |= Overlayable::Policy::kProductServices;
}
const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
@@ -478,13 +478,11 @@
return false;
}
- for (Overlayable::Policy policy : policies) {
- Overlayable overlayable;
- overlayable.source = source_.WithLine(0);
- overlayable.policy = policy;
- if (!table_->AddOverlayable(iter->second, overlayable, diag_)) {
- return false;
- }
+ Overlayable overlayable{};
+ overlayable.source = source_.WithLine(0);
+ overlayable.policies = policies;
+ if (!table_->SetOverlayable(iter->second, overlayable, diag_)) {
+ return false;
}
}
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 976c328..200e2d4 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -429,56 +429,52 @@
CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
for (auto& entry : type->entries) {
CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
-
- // TODO(b/120298168): Convert the policies vector to a policy set or bitmask
- if (!entry->overlayable_declarations.empty()) {
- uint16_t policy_flags = 0;
- for (Overlayable overlayable : entry->overlayable_declarations) {
- if (overlayable.policy) {
- switch (overlayable.policy.value()) {
- case Overlayable::Policy::kPublic:
- policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
- break;
- case Overlayable::Policy::kSystem:
- policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
- break;
- case Overlayable::Policy::kVendor:
- policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
- break;
- case Overlayable::Policy::kProduct:
- policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
- break;
- case Overlayable::Policy::kProductServices:
- policy_flags |=
- ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
- break;
- }
- } else {
- // Encode overlayable entries defined without a policy as publicly overlayable
- policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
- }
- }
-
- // Find the overlayable policy chunk with the same policies as the entry
- PolicyChunk* policy_chunk = nullptr;
- for (PolicyChunk& policy : policies) {
- if (policy.policy_flags == policy_flags) {
- policy_chunk = &policy;
- break;
- }
- }
-
- // Create a new policy chunk if an existing one with the same policy cannot be found
- if (policy_chunk == nullptr) {
- PolicyChunk p;
- p.policy_flags = policy_flags;
- policies.push_back(p);
- policy_chunk = &policies.back();
- }
-
- policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(),
- entry->id.value()));
+ if (!entry->overlayable) {
+ continue;
}
+
+ Overlayable overlayable = entry->overlayable.value();
+ uint32_t policy_flags = Overlayable::Policy::kNone;
+ if (overlayable.policies & Overlayable::Policy::kPublic) {
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+ }
+ if (overlayable.policies & Overlayable::Policy::kSystem) {
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+ }
+ if (overlayable.policies & Overlayable::Policy::kVendor) {
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+ }
+ if (overlayable.policies & Overlayable::Policy::kProduct) {
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+ }
+ if (overlayable.policies & Overlayable::Policy::kProductServices) {
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
+ }
+
+ if (overlayable.policies == Overlayable::Policy::kNone) {
+ // Encode overlayable entries defined without a policy as publicly overlayable
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+ }
+
+ // Find the overlayable policy chunk with the same policies as the entry
+ PolicyChunk* policy_chunk = nullptr;
+ for (PolicyChunk& policy : policies) {
+ if (policy.policy_flags == policy_flags) {
+ policy_chunk = &policy;
+ break;
+ }
+ }
+
+ // Create a new policy chunk if an existing one with the same policy cannot be found
+ if (policy_chunk == nullptr) {
+ PolicyChunk p;
+ p.policy_flags = policy_flags;
+ policies.push_back(p);
+ policy_chunk = &policies.back();
+ }
+
+ policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(),
+ entry->id.value()));
}
}
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 410efbe..e99ab1f 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,14 +628,17 @@
}
TEST_F(TableFlattenerTest, FlattenOverlayable) {
+ Overlayable overlayable{};
+ overlayable.policies |= Overlayable::Policy::kProduct;
+ overlayable.policies |= Overlayable::Policy::kSystem;
+ overlayable.policies |= Overlayable::Policy::kVendor;
+
std::string name = "com.app.test:integer/overlayable";
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
.AddSimple(name, ResourceId(0x7f020000))
- .AddOverlayable(name, Overlayable::Policy::kProduct)
- .AddOverlayable(name, Overlayable::Policy::kSystem)
- .AddOverlayable(name, Overlayable::Policy::kVendor)
+ .SetOverlayable(name, overlayable)
.Build();
ResourceTable output_table;
@@ -644,39 +647,45 @@
auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
- Overlayable::Policy::kSystem);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
- Overlayable::Policy::kVendor);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
- Overlayable::Policy::kProduct);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
+ | Overlayable::Policy::kVendor
+ | Overlayable::Policy::kProduct);
}
TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
std::string name_zero = "com.app.test:integer/overlayable_zero";
+ Overlayable overlayable_zero{};
+ overlayable_zero.policies |= Overlayable::Policy::kProduct;
+ overlayable_zero.policies |= Overlayable::Policy::kSystem;
+ overlayable_zero.policies |= Overlayable::Policy::kProductServices;
+
std::string name_one = "com.app.test:integer/overlayable_one";
+ Overlayable overlayable_one{};
+ overlayable_one.policies |= Overlayable::Policy::kPublic;
+ overlayable_one.policies |= Overlayable::Policy::kProductServices;
+
std::string name_two = "com.app.test:integer/overlayable_two";
+ Overlayable overlayable_two{};
+ overlayable_two.policies |= Overlayable::Policy::kProduct;
+ overlayable_two.policies |= Overlayable::Policy::kSystem;
+ overlayable_two.policies |= Overlayable::Policy::kVendor;
+
std::string name_three = "com.app.test:integer/overlayable_three";
+ Overlayable overlayable_three{};
+
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
.AddSimple(name_zero, ResourceId(0x7f020000))
- .AddOverlayable(name_zero, Overlayable::Policy::kProduct)
- .AddOverlayable(name_zero, Overlayable::Policy::kSystem)
- .AddOverlayable(name_zero, Overlayable::Policy::kProductServices)
+ .SetOverlayable(name_zero, overlayable_zero)
.AddSimple(name_one, ResourceId(0x7f020001))
- .AddOverlayable(name_one, Overlayable::Policy::kPublic)
- .AddOverlayable(name_one, Overlayable::Policy::kSystem)
+ .SetOverlayable(name_one, overlayable_one)
.AddSimple(name_two, ResourceId(0x7f020002))
- .AddOverlayable(name_two, Overlayable::Policy::kProduct)
- .AddOverlayable(name_two, Overlayable::Policy::kSystem)
- .AddOverlayable(name_two, Overlayable::Policy::kProductServices)
+ .SetOverlayable(name_two, overlayable_two)
.AddSimple(name_three, ResourceId(0x7f020003))
- .AddOverlayable(name_three, {})
+ .SetOverlayable(name_three, overlayable_three)
.Build();
ResourceTable output_table;
@@ -685,51 +694,35 @@
auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
- Overlayable::Policy::kSystem);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
- Overlayable::Policy::kProduct);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
- Overlayable::Policy::kProductServices);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
+ | Overlayable::Policy::kProduct
+ | Overlayable::Policy::kProductServices);
search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 2);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
- Overlayable::Policy::kPublic);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
- Overlayable::Policy::kSystem);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic
+ | Overlayable::Policy::kProductServices);
search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
- Overlayable::Policy::kSystem);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
- Overlayable::Policy::kProduct);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
- Overlayable::Policy::kProductServices);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
+ | Overlayable::Policy::kProduct
+ | Overlayable::Policy::kVendor);
search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 1);
- EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
- EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
- Overlayable::Policy::kPublic);
-
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic);
}
-
} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index f612914..cf2ab0f 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -437,37 +437,39 @@
entry->allow_new = std::move(allow_new);
}
- for (const pb::Overlayable& pb_overlayable : pb_entry.overlayable()) {
- Overlayable overlayable;
- switch (pb_overlayable.policy()) {
- case pb::Overlayable::NONE:
- overlayable.policy = {};
- break;
- case pb::Overlayable::PUBLIC:
- overlayable.policy = Overlayable::Policy::kPublic;
- break;
- case pb::Overlayable::PRODUCT:
- overlayable.policy = Overlayable::Policy::kProduct;
- break;
- case pb::Overlayable::PRODUCT_SERVICES:
- overlayable.policy = Overlayable::Policy::kProductServices;
- break;
- case pb::Overlayable::SYSTEM:
- overlayable.policy = Overlayable::Policy::kSystem;
- break;
- case pb::Overlayable::VENDOR:
- overlayable.policy = Overlayable::Policy::kVendor;
- break;
- default:
- *out_error = "unknown overlayable policy";
- return false;
+ if (pb_entry.has_overlayable()) {
+ Overlayable overlayable{};
+
+ const pb::Overlayable& pb_overlayable = pb_entry.overlayable();
+ for (const int policy : pb_overlayable.policy()) {
+ switch (policy) {
+ case pb::Overlayable::PUBLIC:
+ overlayable.policies |= Overlayable::Policy::kPublic;
+ break;
+ case pb::Overlayable::SYSTEM:
+ overlayable.policies |= Overlayable::Policy::kSystem;
+ break;
+ case pb::Overlayable::VENDOR:
+ overlayable.policies |= Overlayable::Policy::kVendor;
+ break;
+ case pb::Overlayable::PRODUCT:
+ overlayable.policies |= Overlayable::Policy::kProduct;
+ break;
+ case pb::Overlayable::PRODUCT_SERVICES:
+ overlayable.policies |= Overlayable::Policy::kProductServices;
+ break;
+ default:
+ *out_error = "unknown overlayable policy";
+ return false;
+ }
}
if (pb_overlayable.has_source()) {
DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source);
}
+
overlayable.comment = pb_overlayable.comment();
- entry->overlayable_declarations.push_back(overlayable);
+ entry->overlayable = overlayable;
}
ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index ecf34d1..70bf868 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -310,26 +310,24 @@
pb_allow_new->set_comment(entry->allow_new.value().comment);
}
- for (const Overlayable& overlayable : entry->overlayable_declarations) {
- pb::Overlayable* pb_overlayable = pb_entry->add_overlayable();
- if (overlayable.policy) {
- switch (overlayable.policy.value()) {
- case Overlayable::Policy::kPublic:
- pb_overlayable->set_policy(pb::Overlayable::PUBLIC);
- break;
- case Overlayable::Policy::kProduct:
- pb_overlayable->set_policy(pb::Overlayable::PRODUCT);
- break;
- case Overlayable::Policy::kProductServices:
- pb_overlayable->set_policy(pb::Overlayable::PRODUCT_SERVICES);
- break;
- case Overlayable::Policy::kSystem:
- pb_overlayable->set_policy(pb::Overlayable::SYSTEM);
- break;
- case Overlayable::Policy::kVendor:
- pb_overlayable->set_policy(pb::Overlayable::VENDOR);
- break;
- }
+ if (entry->overlayable) {
+ pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable();
+
+ Overlayable overlayable = entry->overlayable.value();
+ if (overlayable.policies & Overlayable::Policy::kPublic) {
+ pb_overlayable->add_policy(pb::Overlayable::PUBLIC);
+ }
+ if (overlayable.policies & Overlayable::Policy::kProduct) {
+ pb_overlayable->add_policy(pb::Overlayable::PRODUCT);
+ }
+ if (overlayable.policies & Overlayable::Policy::kProductServices) {
+ pb_overlayable->add_policy(pb::Overlayable::PRODUCT_SERVICES);
+ }
+ if (overlayable.policies & Overlayable::Policy::kSystem) {
+ pb_overlayable->add_policy(pb::Overlayable::SYSTEM);
+ }
+ if (overlayable.policies & Overlayable::Policy::kVendor) {
+ pb_overlayable->add_policy(pb::Overlayable::VENDOR);
}
SerializeSourceToPb(overlayable.source, &source_pool,
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 1cd2f0b..fb913f40 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -93,7 +93,7 @@
util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
// Make an overlayable resource.
- ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
+ ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
Overlayable{}, test::GetDiagnostics()));
pb::ResourceTable pb_table;
@@ -160,8 +160,9 @@
new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
+ Eq(Overlayable::Policy::kNone));
}
TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
@@ -502,15 +503,26 @@
}
TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
+ Overlayable overlayable_foo{};
+ overlayable_foo.policies |= Overlayable::Policy::kSystem;
+ overlayable_foo.policies |= Overlayable::Policy::kProduct;
+
+ Overlayable overlayable_bar{};
+ overlayable_bar.policies |= Overlayable::Policy::kProductServices;
+ overlayable_bar.policies |= Overlayable::Policy::kVendor;
+
+ Overlayable overlayable_baz{};
+ overlayable_baz.policies |= Overlayable::Policy::kPublic;
+
+ Overlayable overlayable_biz{};
+
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .AddOverlayable("com.app.a:bool/foo", Overlayable::Policy::kSystem)
- .AddOverlayable("com.app.a:bool/foo", Overlayable::Policy::kProduct)
- .AddOverlayable("com.app.a:bool/bar", Overlayable::Policy::kProductServices)
- .AddOverlayable("com.app.a:bool/bar", Overlayable::Policy::kVendor)
- .AddOverlayable("com.app.a:bool/baz", Overlayable::Policy::kPublic)
- .AddOverlayable("com.app.a:bool/biz", {})
+ .SetOverlayable("com.app.a:bool/foo", overlayable_foo)
+ .SetOverlayable("com.app.a:bool/bar", overlayable_bar)
+ .SetOverlayable("com.app.a:bool/baz", overlayable_baz)
+ .SetOverlayable("com.app.a:bool/biz", overlayable_biz)
.AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
.Build();
@@ -523,37 +535,36 @@
ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
EXPECT_THAT(error, IsEmpty());
- Maybe<ResourceTable::SearchResult> result =
+ Maybe<ResourceTable::SearchResult> search_result =
new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
- EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kSystem));
- EXPECT_THAT(result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kProduct));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kSystem
+ | Overlayable::Policy::kProduct));
- result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
- EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProductServices));
- EXPECT_THAT(result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kVendor));
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProductServices
+ | Overlayable::Policy::kVendor));
- result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kPublic));
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kPublic);
- result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
- EXPECT_FALSE(result.value().entry->overlayable_declarations[0].policy);
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kNone);
- result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
- ASSERT_TRUE(result);
- EXPECT_THAT(result.value().entry->overlayable_declarations.size(), Eq(0));
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
+ ASSERT_TRUE(search_result);
+ ASSERT_FALSE(search_result.value().entry->overlayable);
}
} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 1b6626a..8cbc037 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -374,8 +374,8 @@
}
// Ensure that definitions for values declared as overlayable exist
- if (!entry->overlayable_declarations.empty() && entry->values.empty()) {
- context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_declarations[0].source)
+ if (entry->overlayable && entry->values.empty()) {
+ context->GetDiagnostics()->Error(DiagMessage(entry->overlayable.value().source)
<< "no definition for overlayable symbol '"
<< name << "'");
error = true;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index d777e22..22e1723 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -134,35 +134,21 @@
dst_entry->allow_new = std::move(src_entry->allow_new);
}
- for (auto& src_overlayable : src_entry->overlayable_declarations) {
- for (auto& dst_overlayable : dst_entry->overlayable_declarations) {
- // An overlayable resource cannot be declared twice with the same policy
- if (src_overlayable.policy == dst_overlayable.policy) {
- context->GetDiagnostics()->Error(DiagMessage(src_overlayable.source)
- << "duplicate overlayable declaration for resource '"
- << src_entry->name << "'");
- context->GetDiagnostics()->Error(DiagMessage(dst_overlayable.source)
- << "previous declaration here");
- return false;
- }
-
- // An overlayable resource cannot be declared once with a policy and without a policy because
- // the policy becomes unused
- if (!src_overlayable.policy || !dst_overlayable.policy) {
- context->GetDiagnostics()->Error(DiagMessage(src_overlayable.source)
- << "overlayable resource '" << src_entry->name
- << "' declared once with a policy and once with no "
- << "policy");
- context->GetDiagnostics()->Error(DiagMessage(dst_overlayable.source)
- << "previous declaration here");
- return false;
- }
+ if (src_entry->overlayable) {
+ if (dst_entry->overlayable) {
+ // Do not allow a resource with an overlayable declaration to have that overlayable
+ // declaration redefined
+ context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source)
+ << "duplicate overlayable declaration for resource '"
+ << src_entry->name << "'");
+ context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source)
+ << "previous declaration here");
+ return false;
+ } else {
+ dst_entry->overlayable = std::move(src_entry->overlayable);
}
}
- dst_entry->overlayable_declarations.insert(dst_entry->overlayable_declarations.end(),
- src_entry->overlayable_declarations.begin(),
- src_entry->overlayable_declarations.end());
return true;
}
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index d6579d3..17b2a83 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -436,17 +436,21 @@
Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")))));
}
-TEST_F(TableMergerTest, AddOverlayable) {
+TEST_F(TableMergerTest, SetOverlayable) {
+ Overlayable overlayable{};
+ overlayable.policies |= Overlayable::Policy::kProduct;
+ overlayable.policies |= Overlayable::Policy::kVendor;
+
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+ .SetOverlayable("bool/foo", overlayable)
.Build();
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProductServices)
+ .AddSimple("bool/foo")
.Build();
ResourceTable final_table;
@@ -457,26 +461,61 @@
ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
- Maybe<ResourceTable::SearchResult> result = final_table.FindResource(name);
- ASSERT_TRUE(result);
- ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
- ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
- Eq(Overlayable::Policy::kProduct));
- ASSERT_THAT(result.value().entry->overlayable_declarations[1].policy,
- Eq(Overlayable::Policy::kProductServices));
+ Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
+ | Overlayable::Policy::kVendor));
}
-TEST_F(TableMergerTest, AddDuplicateOverlayableFail) {
+TEST_F(TableMergerTest, SetOverlayableLater) {
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+ .AddSimple("bool/foo")
.Build();
+ Overlayable overlayable{};
+ overlayable.policies |= Overlayable::Policy::kPublic;
+ overlayable.policies |= Overlayable::Policy::kProductServices;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+ .SetOverlayable("bool/foo", overlayable)
+ .Build();
+
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = true;
+ TableMerger merger(context_.get(), &final_table, options);
+ ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+ ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
+
+ const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
+ Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable);
+ Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
+ EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kPublic
+ | Overlayable::Policy::kProductServices));
+}
+
+TEST_F(TableMergerTest, SetOverlayableSamePolicesFail) {
+ Overlayable overlayable_first{};
+ overlayable_first.policies |= Overlayable::Policy::kProduct;
+ std::unique_ptr<ResourceTable> table_a =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .SetOverlayable("bool/foo", overlayable_first)
+ .Build();
+
+ Overlayable overlayable_second{};
+ overlayable_second.policies |= Overlayable::Policy::kProduct;
+ std::unique_ptr<ResourceTable> table_b =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .SetOverlayable("bool/foo", overlayable_second)
.Build();
ResourceTable final_table;
@@ -487,38 +526,21 @@
ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
}
-TEST_F(TableMergerTest, AddOverlayablePolicyAndNoneFirstFail) {
+TEST_F(TableMergerTest, SetOverlayableDifferentPolicesFail) {
+ Overlayable overlayable_first{};
+ overlayable_first.policies |= Overlayable::Policy::kVendor;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", {})
+ .SetOverlayable("bool/foo",overlayable_first)
.Build();
+ Overlayable overlayable_second{};
+ overlayable_second.policies |= Overlayable::Policy::kProduct;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
- .Build();
-
- ResourceTable final_table;
- TableMergerOptions options;
- options.auto_add_overlay = true;
- TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
- ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
-}
-
-TEST_F(TableMergerTest, AddOverlayablePolicyAndNoneLastFail) {
- std::unique_ptr<ResourceTable> table_a =
- test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
- .Build();
-
- std::unique_ptr<ResourceTable> table_b =
- test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
- .AddOverlayable("bool/foo", {})
+ .SetOverlayable("bool/foo", overlayable_second)
.Build();
ResourceTable final_table;
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 2e717ff..9c5b5d3 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -248,7 +248,7 @@
if (!split_entry->id) {
split_entry->id = entry->id;
split_entry->visibility = entry->visibility;
- split_entry->overlayable_declarations = entry->overlayable_declarations;
+ split_entry->overlayable = entry->overlayable;
}
// Copy the selected values into the new Split Entry.
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 03b59e0..884ec38 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -135,12 +135,11 @@
return *this;
}
-ResourceTableBuilder& ResourceTableBuilder::AddOverlayable(const StringPiece& name,
- const Maybe<Overlayable::Policy> p) {
+ResourceTableBuilder& ResourceTableBuilder::SetOverlayable(const StringPiece& name,
+ const Overlayable& overlayable) {
+
ResourceName res_name = ParseNameOrDie(name);
- Overlayable overlayable;
- overlayable.policy = p;
- CHECK(table_->AddOverlayable(res_name, overlayable, GetDiagnostics()));
+ CHECK(table_->SetOverlayable(res_name, overlayable, GetDiagnostics()));
return *this;
}
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index d68c24d..a120484 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -73,8 +73,8 @@
const ResourceId& id, std::unique_ptr<Value> value);
ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
Visibility::Level level, bool allow_new = false);
- ResourceTableBuilder& AddOverlayable(const android::StringPiece& name,
- Maybe<Overlayable::Policy> policy);
+ ResourceTableBuilder& SetOverlayable(const android::StringPiece& name,
+ const Overlayable& overlayable);
StringPool* string_pool();
std::unique_ptr<ResourceTable> Build();
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index 9dceba2..6b4c346 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -155,7 +155,7 @@
print
print 'std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({'
for locale in sorted(representative_locales):
- print ' 0x%08Xllu, // %s' % (
+ print ' 0x%08XLLU, // %s' % (
pack_to_uint64(locale),
locale)
print '});'
diff --git a/tools/processors/view_inspector/OWNERS b/tools/processors/view_inspector/OWNERS
new file mode 100644
index 0000000..0473f54
--- /dev/null
+++ b/tools/processors/view_inspector/OWNERS
@@ -0,0 +1,3 @@
+alanv@google.com
+ashleyrose@google.com
+aurimas@google.com
\ No newline at end of file
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
index f0b0ff6..579745d 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
@@ -18,16 +18,20 @@
import com.squareup.javapoet.ClassName;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Optional;
/**
* Model of an inspectable class derived from annotations.
*
- * This class does not use any {javax.lang.model} objects to facilitate building models for testing
- * {@link InspectionCompanionGenerator}.
+ * This class does not use any {@code javax.lang.model} objects to facilitate building models for
+ * testing {@link InspectionCompanionGenerator}.
*/
public final class InspectableClassModel {
private final ClassName mClassName;
+ private final Map<String, Property> mPropertyMap;
private Optional<String> mNodeName = Optional.empty();
/**
@@ -35,6 +39,7 @@
*/
public InspectableClassModel(ClassName className) {
mClassName = className;
+ mPropertyMap = new HashMap<>();
}
public ClassName getClassName() {
@@ -48,4 +53,148 @@
public void setNodeName(Optional<String> nodeName) {
mNodeName = nodeName;
}
+
+ /**
+ * Add a property to the model, replacing an existing property of the same name.
+ *
+ * @param property The property to add or replace
+ */
+ public void putProperty(Property property) {
+ mPropertyMap.put(property.getName(), property);
+ }
+
+ /**
+ * Get a property by name.
+ *
+ * @param name The name of the property
+ * @return The property or an empty optional
+ */
+ public Optional<Property> getProperty(String name) {
+ return Optional.of(mPropertyMap.get(name));
+ }
+
+ /**
+ * Get all the properties defined on this model.
+ *
+ * @return An un-ordered collection of properties
+ */
+ public Collection<Property> getAllProperties() {
+ return mPropertyMap.values();
+ }
+
+ /**
+ * Model an inspectable property
+ */
+ public static final class Property {
+ private final String mName;
+ private String mGetter;
+ private Type mType;
+ private boolean mAttributeIdInferrableFromR = true;
+ private int mAttributeId = 0;
+
+ public Property(String name) {
+ mName = name;
+ }
+
+ public int getAttributeId() {
+ return mAttributeId;
+ }
+
+ /**
+ * Set the attribute ID, and mark the attribute ID as non-inferrable.
+ *
+ * @param attributeId The attribute ID for this property
+ */
+ public void setAttributeId(int attributeId) {
+ mAttributeIdInferrableFromR = false;
+ mAttributeId = attributeId;
+ }
+
+ public boolean isAttributeIdInferrableFromR() {
+ return mAttributeIdInferrableFromR;
+ }
+
+ public void setAttributeIdInferrableFromR(boolean attributeIdInferrableFromR) {
+ mAttributeIdInferrableFromR = attributeIdInferrableFromR;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getGetter() {
+ return mGetter;
+ }
+
+ public void setGetter(String getter) {
+ mGetter = getter;
+ }
+
+ public Type getType() {
+ return mType;
+ }
+
+ public void setType(Type type) {
+ mType = type;
+ }
+
+ public enum Type {
+ /** Primitive or boxed {@code boolean} */
+ BOOLEAN,
+
+ /** Primitive or boxed {@code byte} */
+ BYTE,
+
+ /** Primitive or boxed {@code char} */
+ CHAR,
+
+ /** Primitive or boxed {@code double} */
+ DOUBLE,
+
+ /** Primitive or boxed {@code float} */
+ FLOAT,
+
+ /** Primitive or boxed {@code int} */
+ INT,
+
+ /** Primitive or boxed {@code long} */
+ LONG,
+
+ /** Primitive or boxed {@code short} */
+ SHORT,
+
+ /** Any other object */
+ OBJECT,
+
+ /**
+ * A color object or packed color {@code int} or {@code long}.
+ *
+ * @see android.graphics.Color
+ * @see android.annotation.ColorInt
+ * @see android.annotation.ColorLong
+ */
+ COLOR,
+
+ /**
+ * An {@code int} packed with a gravity specification
+ *
+ * @see android.view.Gravity
+ */
+ GRAVITY,
+
+ /**
+ * An enumeration packed into an {@code int}.
+ *
+ * @see android.view.inspector.IntEnumMapping
+ */
+ INT_ENUM,
+
+ /**
+ * Non-exclusive or partially-exclusive flags packed into an {@code int}.
+ *
+ * @see android.view.inspector.IntFlagMapping
+ */
+ INT_FLAG
+ }
+ }
}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
index fe0153d..3b85dbb 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
@@ -16,13 +16,23 @@
package android.processor.view.inspector;
+import android.processor.view.inspector.InspectableClassModel.Property;
+
import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.NameAllocator;
import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.NoSuchElementException;
import java.util.Optional;
import javax.annotation.processing.Filer;
@@ -36,10 +46,62 @@
private final Class mRequestingClass;
/**
+ * The class name for {@code R.java}.
+ */
+ private static final ClassName R_CLASS_NAME = ClassName.get("android", "R");
+
+ /**
+ * The class name of {@link android.content.res.ResourceId}.
+ */
+ private static final ClassName RESOURCE_ID_CLASS_NAME = ClassName.get(
+ "android.content.res", "ResourceId");
+
+ /**
+ * The class name of {@link android.view.inspector.InspectionCompanion}.
+ */
+ private static final ClassName INSPECTION_COMPANION = ClassName.get(
+ "android.view.inspector", "InspectionCompanion");
+
+ /**
+ * The class name of {@link android.view.inspector.PropertyMapper}.
+ */
+ private static final ClassName PROPERTY_MAPPER = ClassName.get(
+ "android.view.inspector", "PropertyMapper");
+
+ /**
+ * The class name of {@link android.view.inspector.PropertyReader}.
+ */
+ private static final ClassName PROPERTY_READER = ClassName.get(
+ "android.view.inspector", "PropertyReader");
+
+ /**
+ * The {@code mPropertiesMapped} field.
+ */
+ private static final FieldSpec M_PROPERTIES_MAPPED = FieldSpec
+ .builder(TypeName.BOOLEAN, "mPropertiesMapped", Modifier.PRIVATE)
+ .initializer("false")
+ .addJavadoc(
+ "Set by {@link #mapProperties($T)} once properties have been mapped.\n",
+ PROPERTY_MAPPER)
+ .build();
+
+ /**
+ * The suffix of the generated class name after the class's binary name.
+ */
+ private static final String GENERATED_CLASS_SUFFIX = "$$InspectionCompanion";
+
+ /**
+ * The null resource ID.
+ *
+ * @see android.content.res.ResourceId#ID_NULL
+ */
+ private static final int NO_ID = 0;
+
+ /**
* @param filer A filer to write the generated source to
* @param requestingClass A class object representing the class that invoked the generator
*/
- public InspectionCompanionGenerator(final Filer filer, final Class requestingClass) {
+ public InspectionCompanionGenerator(Filer filer, Class requestingClass) {
mFiler = filer;
mRequestingClass = requestingClass;
}
@@ -77,17 +139,24 @@
* @return A TypeSpec of the inspection companion
*/
private TypeSpec generateTypeSpec(InspectableClassModel model) {
+ final List<PropertyIdField> propertyIdFields = generatePropertyIdFields(model);
+
TypeSpec.Builder builder = TypeSpec
.classBuilder(generateClassName(model))
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addSuperinterface(ParameterizedTypeName.get(
- ClassName.get("android.view.inspector", "InspectionCompanion"),
- model.getClassName()))
+ INSPECTION_COMPANION, model.getClassName()))
.addJavadoc("Inspection companion for {@link $T}.\n\n", model.getClassName())
.addJavadoc("Generated by {@link $T}\n", getClass())
.addJavadoc("on behalf of {@link $T}.\n", mRequestingClass)
- .addMethod(generateMapProperties(model))
- .addMethod(generateReadProperties(model));
+ .addField(M_PROPERTIES_MAPPED);
+
+ for (PropertyIdField propertyIdField : propertyIdFields) {
+ builder.addField(propertyIdField.mFieldSpec);
+ }
+
+ builder.addMethod(generateMapProperties(propertyIdFields))
+ .addMethod(generateReadProperties(model, propertyIdFields));
generateGetNodeName(model).ifPresent(builder::addMethod);
@@ -95,6 +164,42 @@
}
/**
+ * Build a list of {@link PropertyIdField}'s for a model.
+ *
+ * To insure idempotency of the generated code, this method sorts the list of properties
+ * alphabetically by name.
+ *
+ * A {@link NameAllocator} is used to ensure that the field names are valid Java identifiers,
+ * and it prevents overlaps in names by suffixing them as needed.
+ *
+ * @param model The model to get properties from
+ * @return A list of properties and fields
+ */
+ private List<PropertyIdField> generatePropertyIdFields(InspectableClassModel model) {
+ final NameAllocator nameAllocator = new NameAllocator();
+ final List<Property> sortedProperties = new ArrayList<>(model.getAllProperties());
+ final List<PropertyIdField> propertyIdFields = new ArrayList<>(sortedProperties.size());
+
+ sortedProperties.sort(Comparator.comparing(Property::getName));
+
+ for (Property property : sortedProperties) {
+ // Format a property to a member field name like "someProperty" -> "mSomePropertyId"
+ final String memberName = String.format(
+ "m%s%sId",
+ property.getName().substring(0, 1).toUpperCase(),
+ property.getName().substring(1));
+ final FieldSpec fieldSpec = FieldSpec
+ .builder(TypeName.INT, nameAllocator.newName(memberName), Modifier.PRIVATE)
+ .addJavadoc("Property ID of {@code $L}.\n", property.getName())
+ .build();
+
+ propertyIdFields.add(new PropertyIdField(fieldSpec, property));
+ }
+
+ return propertyIdFields;
+ }
+
+ /**
* Generate a method definition for
* {@link android.view.inspector.InspectionCompanion#getNodeName()}, if needed.
*
@@ -119,21 +224,19 @@
* {@link android.view.inspector.InspectionCompanion#mapProperties(
* android.view.inspector.PropertyMapper)}.
*
- * TODO: implement
- *
- * @param model The model to generate from
+ * @param propertyIdFields A list of properties to map to ID fields
* @return The method definition
*/
- private MethodSpec generateMapProperties(InspectableClassModel model) {
- final ClassName propertyMapper = ClassName.get(
- "android.view.inspector", "PropertyMapper");
-
- return MethodSpec.methodBuilder("mapProperties")
+ private MethodSpec generateMapProperties(List<PropertyIdField> propertyIdFields) {
+ final MethodSpec.Builder builder = MethodSpec.methodBuilder("mapProperties")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
- .addParameter(propertyMapper, "propertyMapper")
- // TODO: add method body
- .build();
+ .addParameter(PROPERTY_MAPPER, "propertyMapper");
+
+ propertyIdFields.forEach(p -> builder.addStatement(generatePropertyMapperInvocation(p)));
+ builder.addStatement("$N = true", M_PROPERTIES_MAPPED);
+
+ return builder.build();
}
/**
@@ -141,21 +244,91 @@
* {@link android.view.inspector.InspectionCompanion#readProperties(
* Object, android.view.inspector.PropertyReader)}.
*
- * TODO: implement
- *
* @param model The model to generate from
+ * @param propertyIdFields A list of properties and ID fields to read from
* @return The method definition
*/
- private MethodSpec generateReadProperties(InspectableClassModel model) {
- final ClassName propertyReader = ClassName.get(
- "android.view.inspector", "PropertyReader");
-
- return MethodSpec.methodBuilder("readProperties")
+ private MethodSpec generateReadProperties(
+ InspectableClassModel model,
+ List<PropertyIdField> propertyIdFields) {
+ final MethodSpec.Builder builder = MethodSpec.methodBuilder("readProperties")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addParameter(model.getClassName(), "inspectable")
- .addParameter(propertyReader, "propertyReader")
- // TODO: add method body
+ .addParameter(PROPERTY_READER, "propertyReader")
+ .addCode(generatePropertyMapInitializationCheck());
+
+ for (PropertyIdField propertyIdField : propertyIdFields) {
+ builder.addStatement(
+ "propertyReader.read$L($N, inspectable.$L())",
+ methodSuffixForPropertyType(propertyIdField.mProperty.getType()),
+ propertyIdField.mFieldSpec,
+ propertyIdField.mProperty.getGetter());
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Generate a statement maps a property with a {@link android.view.inspector.PropertyMapper}.
+ *
+ * @param propertyIdField The property model and ID field to generate from
+ * @return A statement that invokes property mapper method
+ */
+ private CodeBlock generatePropertyMapperInvocation(PropertyIdField propertyIdField) {
+ final CodeBlock.Builder builder = CodeBlock.builder();
+ final Property property = propertyIdField.mProperty;
+ final FieldSpec fieldSpec = propertyIdField.mFieldSpec;
+
+ builder.add(
+ "$N = propertyMapper.map$L($S,$W",
+ fieldSpec,
+ methodSuffixForPropertyType(property.getType()),
+ property.getName());
+
+ if (property.isAttributeIdInferrableFromR()) {
+ builder.add("$T.attr.$L", R_CLASS_NAME, property.getName());
+ } else {
+ if (property.getAttributeId() == NO_ID) {
+ builder.add("$T.ID_NULL", RESOURCE_ID_CLASS_NAME);
+ } else {
+ builder.add("$L", String.format("0x%08x", property.getAttributeId()));
+ }
+ }
+
+ switch (property.getType()) {
+ case INT_ENUM:
+ throw new RuntimeException("IntEnumMapping generation not implemented");
+ case INT_FLAG:
+ throw new RuntimeException("IntFlagMapping generation not implemented");
+ default:
+ builder.add(")");
+ break;
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Generate a check that throws
+ * {@link android.view.inspector.InspectionCompanion.UninitializedPropertyMapException}
+ * if the properties haven't been initialized.
+ *
+ * <pre>
+ * if (!mPropertiesMapped) {
+ * throw new InspectionCompanion.UninitializedPropertyMapException();
+ * }
+ * </pre>
+ *
+ * @return A codeblock containing the property map initialization check
+ */
+ private CodeBlock generatePropertyMapInitializationCheck() {
+ return CodeBlock.builder()
+ .beginControlFlow("if (!$N)", M_PROPERTIES_MAPPED)
+ .addStatement(
+ "throw new $T()",
+ INSPECTION_COMPANION.nestedClass("UninitializedPropertyMapException"))
+ .endControlFlow()
.build();
}
@@ -163,19 +336,71 @@
* Generate the final class name for the inspection companion from the model's class name.
*
* The generated class is added to the same package as the source class. If the class in the
- * model is a nested class, the nested class names are joined with {"$"}. The suffix
- * {"$$InspectionCompanion"} is always added the the generated name. E.g.: For modeled class
- * {com.example.Outer.Inner}, the generated class name will be
- * {com.example.Outer$Inner$$InspectionCompanion}.
+ * model is a nested class, the nested class names are joined with {@code "$"}. The suffix
+ * {@code "$$InspectionCompanion"} is always added the the generated name. E.g.: For modeled
+ * class {@code com.example.Outer.Inner}, the generated class name will be
+ * {@code com.example.Outer$Inner$$InspectionCompanion}.
*
* @param model The model to generate from
* @return A class name for the generated inspection companion class
*/
- private ClassName generateClassName(final InspectableClassModel model) {
+ private static ClassName generateClassName(InspectableClassModel model) {
final ClassName className = model.getClassName();
return ClassName.get(
className.packageName(),
- String.join("$", className.simpleNames()) + "$$InspectionCompanion");
+ String.join("$", className.simpleNames()) + GENERATED_CLASS_SUFFIX);
+ }
+
+ /**
+ * Get the suffix for a {@code map} or {@code read} method for a property type.
+ *
+ * @param type The requested property type
+ * @return A method suffix
+ */
+ private static String methodSuffixForPropertyType(Property.Type type) {
+ switch (type) {
+ case BOOLEAN:
+ return "Boolean";
+ case BYTE:
+ return "Byte";
+ case CHAR:
+ return "Char";
+ case DOUBLE:
+ return "Double";
+ case FLOAT:
+ return "Float";
+ case INT:
+ return "Int";
+ case LONG:
+ return "Long";
+ case SHORT:
+ return "Short";
+ case OBJECT:
+ return "Object";
+ case COLOR:
+ return "Color";
+ case GRAVITY:
+ return "Gravity";
+ case INT_ENUM:
+ return "IntEnum";
+ case INT_FLAG:
+ return "IntFlag";
+ default:
+ throw new NoSuchElementException(String.format("No such property type, %s", type));
+ }
+ }
+
+ /**
+ * Value class that holds a {@link Property} and a {@link FieldSpec} for that property.
+ */
+ private static final class PropertyIdField {
+ private final FieldSpec mFieldSpec;
+ private final Property mProperty;
+
+ private PropertyIdField(FieldSpec fieldSpec, Property property) {
+ mFieldSpec = fieldSpec;
+ mProperty = property;
+ }
}
}
diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
index c02b0bd..f639719 100644
--- a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
+++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
@@ -16,6 +16,8 @@
package android.processor.view.inspector;
+import android.processor.view.inspector.InspectableClassModel.Property;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.fail;
@@ -61,6 +63,54 @@
assertGeneratedFileEquals("NestedClass");
}
+ @Test
+ public void testSimpleProperties() {
+ addProperty("boolean", Property.Type.BOOLEAN, "getBoolean");
+ addProperty("byte", Property.Type.BYTE, "getByte");
+ addProperty("char", Property.Type.CHAR, "getChar");
+ addProperty("double", Property.Type.DOUBLE, "getDouble");
+ addProperty("float", Property.Type.FLOAT, "getFloat");
+ addProperty("int", Property.Type.INT, "getInt");
+ addProperty("long", Property.Type.LONG, "getLong");
+ addProperty("short", Property.Type.SHORT, "getShort");
+
+ addProperty("object", Property.Type.OBJECT, "getObject");
+ addProperty("color", Property.Type.COLOR, "getColor");
+ addProperty("gravity", Property.Type.GRAVITY, "getGravity");
+
+ assertGeneratedFileEquals("SimpleProperties");
+ }
+
+ @Test
+ public void testNoAttributeId() {
+ final Property property = new Property("noAttributeProperty");
+ property.setType(Property.Type.INT);
+ property.setGetter("getNoAttributeProperty");
+ property.setAttributeIdInferrableFromR(false);
+ mModel.putProperty(property);
+
+ assertGeneratedFileEquals("NoAttributeId");
+ }
+
+ @Test
+ public void testSuppliedAttributeId() {
+ final Property property = new Property("suppliedAttributeProperty");
+ property.setType(Property.Type.INT);
+ property.setGetter("getSuppliedAttributeProperty");
+ property.setAttributeId(0xdecafbad);
+ mModel.putProperty(property);
+
+ assertGeneratedFileEquals("SuppliedAttributeId");
+ }
+
+ private Property addProperty(String name, Property.Type type, String getter) {
+ final Property property = new Property(name);
+ property.setType(type);
+ property.setGetter(getter);
+ mModel.putProperty(property);
+ return property;
+ }
+
private void assertGeneratedFileEquals(String fileName) {
assertEquals(
loadTextResource(String.format(RESOURCE_PATH_TEMPLATE, fileName)),
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
index e5fb6a2..2fc242c 100644
--- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
@@ -12,11 +12,20 @@
* on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
*/
public final class Outer$Inner$$InspectionCompanion implements InspectionCompanion<Outer.Inner> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
@Override
public void mapProperties(PropertyMapper propertyMapper) {
+ mPropertiesMapped = true;
}
@Override
public void readProperties(Outer.Inner inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
}
}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
new file mode 100644
index 0000000..277e840
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
@@ -0,0 +1,39 @@
+package com.android.inspectable;
+
+import android.content.res.ResourceId;
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code noAttributeProperty}.
+ */
+ private int mNoAttributePropertyId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mNoAttributePropertyId = propertyMapper.mapInt("noAttributeProperty", ResourceId.ID_NULL);
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readInt(mNoAttributePropertyId, inspectable.getNoAttributeProperty());
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
index a334f50..1142548 100644
--- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
@@ -13,12 +13,21 @@
* on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
*/
public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
@Override
public void mapProperties(PropertyMapper propertyMapper) {
+ mPropertiesMapped = true;
}
@Override
public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
}
@Override
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
new file mode 100644
index 0000000..57eb080
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
@@ -0,0 +1,109 @@
+package com.android.inspectable;
+
+import android.R;
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code boolean}.
+ */
+ private int mBooleanId;
+
+ /**
+ * Property ID of {@code byte}.
+ */
+ private int mByteId;
+
+ /**
+ * Property ID of {@code char}.
+ */
+ private int mCharId;
+
+ /**
+ * Property ID of {@code color}.
+ */
+ private int mColorId;
+
+ /**
+ * Property ID of {@code double}.
+ */
+ private int mDoubleId;
+
+ /**
+ * Property ID of {@code float}.
+ */
+ private int mFloatId;
+
+ /**
+ * Property ID of {@code gravity}.
+ */
+ private int mGravityId;
+
+ /**
+ * Property ID of {@code int}.
+ */
+ private int mIntId;
+
+ /**
+ * Property ID of {@code long}.
+ */
+ private int mLongId;
+
+ /**
+ * Property ID of {@code object}.
+ */
+ private int mObjectId;
+
+ /**
+ * Property ID of {@code short}.
+ */
+ private int mShortId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mBooleanId = propertyMapper.mapBoolean("boolean", R.attr.boolean);
+ mByteId = propertyMapper.mapByte("byte", R.attr.byte);
+ mCharId = propertyMapper.mapChar("char", R.attr.char);
+ mColorId = propertyMapper.mapColor("color", R.attr.color);
+ mDoubleId = propertyMapper.mapDouble("double", R.attr.double);
+ mFloatId = propertyMapper.mapFloat("float", R.attr.float);
+ mGravityId = propertyMapper.mapGravity("gravity", R.attr.gravity);
+ mIntId = propertyMapper.mapInt("int", R.attr.int);
+ mLongId = propertyMapper.mapLong("long", R.attr.long);
+ mObjectId = propertyMapper.mapObject("object", R.attr.object);
+ mShortId = propertyMapper.mapShort("short", R.attr.short);
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readBoolean(mBooleanId, inspectable.getBoolean());
+ propertyReader.readByte(mByteId, inspectable.getByte());
+ propertyReader.readChar(mCharId, inspectable.getChar());
+ propertyReader.readColor(mColorId, inspectable.getColor());
+ propertyReader.readDouble(mDoubleId, inspectable.getDouble());
+ propertyReader.readFloat(mFloatId, inspectable.getFloat());
+ propertyReader.readGravity(mGravityId, inspectable.getGravity());
+ propertyReader.readInt(mIntId, inspectable.getInt());
+ propertyReader.readLong(mLongId, inspectable.getLong());
+ propertyReader.readObject(mObjectId, inspectable.getObject());
+ propertyReader.readShort(mShortId, inspectable.getShort());
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt
new file mode 100644
index 0000000..6b6ce21
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt
@@ -0,0 +1,39 @@
+package com.android.inspectable;
+
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code suppliedAttributeProperty}.
+ */
+ private int mSuppliedAttributePropertyId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mSuppliedAttributePropertyId = propertyMapper.mapInt("suppliedAttributeProperty",
+ 0xdecafbad);
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readInt(mSuppliedAttributePropertyId, inspectable.getSuppliedAttributeProperty());
+ }
+}
diff --git a/tools/signedconfig/debug_key.pem b/tools/signedconfig/debug_key.pem
new file mode 100644
index 0000000..0af577b
--- /dev/null
+++ b/tools/signedconfig/debug_key.pem
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIEfgtO+KPOoqJqTnqkDDKkAcOzyvtovsUO/ShLE6y4XRoAoGCCqGSM49
+AwEHoUQDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60pj1pnU8
+SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==
+-----END EC PRIVATE KEY-----
diff --git a/tools/signedconfig/debug_public.pem b/tools/signedconfig/debug_public.pem
new file mode 100644
index 0000000..f61f813
--- /dev/null
+++ b/tools/signedconfig/debug_public.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoE
+CGbTEBTKKvdd2hO60pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==
+-----END PUBLIC KEY-----
diff --git a/tools/signedconfig/debug_sign.sh b/tools/signedconfig/debug_sign.sh
new file mode 100755
index 0000000..28e5428
--- /dev/null
+++ b/tools/signedconfig/debug_sign.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+# Script to sign data with the debug keys. Outputs base64 for embedding into
+# APK metadata.
+
+openssl dgst -sha256 -sign $(dirname $0)/debug_key.pem $1 | base64 -w 0
+echo
diff --git a/tools/signedconfig/gen_priv_key.sh b/tools/signedconfig/gen_priv_key.sh
new file mode 100755
index 0000000..834c86b
--- /dev/null
+++ b/tools/signedconfig/gen_priv_key.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# This script acts as a record of how the debug key was generated. There should
+# be no need to run it again.
+
+openssl ecparam -name prime256v1 -genkey -noout -out debug_key.pem
+openssl ec -in debug_key.pem -pubout -out debug_public.pem
diff --git a/tools/signedconfig/verify_b64.sh b/tools/signedconfig/verify_b64.sh
new file mode 100755
index 0000000..8e1f58c
--- /dev/null
+++ b/tools/signedconfig/verify_b64.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Script to verify signatures, with both signature & data given in b64
+# Args:
+# 1. data (base64 encoded)
+# 2. signature (base64 encoded)
+# The arg values can be taken from the debug log for SignedConfigService when verbose logging is
+# enabled.
+
+openssl dgst -sha256 -verify $(dirname $0)/debug_public.pem -signature <(echo $2 | base64 -d) <(echo $1 | base64 -d)
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index c6acd02..21d6b94 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -193,5 +193,7 @@
int addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
+
+ String[] getFactoryMacAddresses();
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index cad6d29..57c97ea 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2062,7 +2062,6 @@
/**
* @return true if this adapter supports Device-to-AP RTT
*/
- @SystemApi
public boolean isDeviceToApRttSupported() {
return isFeatureSupported(WIFI_FEATURE_D2AP_RTT);
}
@@ -4435,4 +4434,19 @@
public boolean isOweSupported() {
return isFeatureSupported(WIFI_FEATURE_OWE);
}
+
+ /**
+ * Gets the factory Wi-Fi MAC addresses.
+ * @return Array of String representing Wi-Fi MAC addresses sorted lexically or an empty Array
+ * if failed.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public String[] getFactoryMacAddresses() {
+ try {
+ return mService.getFactoryMacAddresses();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index 0f4e3a8..36f66aa 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -452,4 +452,9 @@
List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public String[] getFactoryMacAddresses() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 13c8c9e..1001b10 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -29,6 +29,7 @@
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -88,6 +89,7 @@
private static final int TEST_UID = 14553;
private static final String TEST_PACKAGE_NAME = "TestPackage";
private static final String TEST_COUNTRY_CODE = "US";
+ private static final String[] TEST_MAC_ADDRESSES = {"da:a1:19:0:0:0"};
@Mock Context mContext;
@Mock
@@ -1320,4 +1322,15 @@
assertEquals(WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP,
mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp());
}
+
+ /**
+ * Verify getting the factory MAC address.
+ * @throws Exception
+ */
+ @Test
+ public void testGetFactoryMacAddress() throws Exception {
+ when(mWifiService.getFactoryMacAddresses()).thenReturn(TEST_MAC_ADDRESSES);
+ assertArrayEquals(TEST_MAC_ADDRESSES, mWifiManager.getFactoryMacAddresses());
+ verify(mWifiService).getFactoryMacAddresses();
+ }
}