Merge "PlayerBase: fix deadlock" into pi-dev
diff --git a/Android.bp b/Android.bp
index 9c76e14..22fe23d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -603,6 +603,7 @@
"core/java/android/content/EventLogTags.logtags",
"core/java/android/speech/tts/EventLogTags.logtags",
"core/java/android/net/EventLogTags.logtags",
+ "core/java/android/os/EventLogTags.logtags",
"core/java/android/webkit/EventLogTags.logtags",
"core/java/com/android/internal/app/EventLogTags.logtags",
"core/java/com/android/internal/logging/EventLogTags.logtags",
diff --git a/api/current.txt b/api/current.txt
index 423e0ae..98711cf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6464,7 +6464,6 @@
method public int getLockTaskFeatures(android.content.ComponentName);
method public java.lang.String[] getLockTaskPackages(android.content.ComponentName);
method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName);
- method public android.content.ComponentName getMandatoryBackupTransport();
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public java.util.List<java.lang.String> getMeteredDataDisabledPackages(android.content.ComponentName);
@@ -6571,7 +6570,6 @@
method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
method public void setLogoutEnabled(android.content.ComponentName, boolean);
method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
- method public boolean setMandatoryBackupTransport(android.content.ComponentName, android.content.ComponentName);
method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -6732,7 +6730,6 @@
field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera";
field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
- field public static final java.lang.String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -7206,8 +7203,6 @@
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static deprecated android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
- method public static deprecated android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
@@ -7297,15 +7292,18 @@
}
public class SliceManager {
- method public android.app.slice.Slice bindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
- method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
+ method public android.app.slice.Slice bindSlice(android.net.Uri, java.util.Set<android.app.slice.SliceSpec>);
+ method public deprecated android.app.slice.Slice bindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.Set<android.app.slice.SliceSpec>);
+ method public deprecated android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int checkSlicePermission(android.net.Uri, int, int);
method public java.util.List<android.net.Uri> getPinnedSlices();
- method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
+ method public java.util.Set<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
method public void grantSlicePermission(java.lang.String, android.net.Uri);
method public android.net.Uri mapIntentToUri(android.content.Intent);
- method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public void pinSlice(android.net.Uri, java.util.Set<android.app.slice.SliceSpec>);
+ method public deprecated void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
method public void revokeSlicePermission(java.lang.String, android.net.Uri);
method public void unpinSlice(android.net.Uri);
field public static final java.lang.String CATEGORY_SLICE = "android.app.slice.category.SLICE";
@@ -7325,7 +7323,8 @@
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
- method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.Set<android.app.slice.SliceSpec>);
+ method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
method public android.app.PendingIntent onCreatePermissionRequest(android.net.Uri);
method public java.util.Collection<android.net.Uri> onGetSliceDescendants(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
@@ -7398,7 +7397,7 @@
public static class NetworkStats.Bucket {
ctor public NetworkStats.Bucket();
- method public int getDefaultNetwork();
+ method public int getDefaultNetworkStatus();
method public long getEndTimeStamp();
method public int getMetered();
method public int getRoaming();
@@ -7474,12 +7473,12 @@
public static final class UsageEvents.Event {
ctor public UsageEvents.Event();
+ method public int getAppStandbyBucket();
method public java.lang.String getClassName();
method public android.content.res.Configuration getConfiguration();
method public int getEventType();
method public java.lang.String getPackageName();
method public java.lang.String getShortcutId();
- method public int getStandbyBucket();
method public long getTimeStamp();
field public static final int CONFIGURATION_CHANGE = 5; // 0x5
field public static final int KEYGUARD_HIDDEN = 18; // 0x12
@@ -10954,7 +10953,7 @@
method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle);
method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean);
method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle);
- method public void onPackagesSuspended(java.lang.String[], android.os.Bundle, android.os.UserHandle);
+ method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle, android.os.Bundle);
method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle);
method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
@@ -11021,7 +11020,7 @@
field public java.lang.String sharedUserId;
field public int sharedUserLabel;
field public deprecated android.content.pm.Signature[] signatures;
- field public android.content.pm.Signature[][] signingCertificateHistory;
+ field public android.content.pm.SigningInfo signingInfo;
field public java.lang.String[] splitNames;
field public int[] splitRevisionCodes;
field public deprecated int versionCode;
@@ -11649,6 +11648,18 @@
field public static final android.os.Parcelable.Creator<android.content.pm.Signature> CREATOR;
}
+ public final class SigningInfo implements android.os.Parcelable {
+ ctor public SigningInfo();
+ ctor public SigningInfo(android.content.pm.SigningInfo);
+ method public int describeContents();
+ method public android.content.pm.Signature[] getApkContentsSigners();
+ method public android.content.pm.Signature[] getSigningCertificateHistory();
+ method public boolean hasMultipleSigners();
+ method public boolean hasPastSigningCertificates();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
+ }
+
public final class VersionedPackage implements android.os.Parcelable {
ctor public VersionedPackage(java.lang.String, int);
ctor public VersionedPackage(java.lang.String, long);
@@ -27202,6 +27213,7 @@
field public static final int NET_CAPABILITY_IMS = 4; // 0x4
field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
field public static final int NET_CAPABILITY_MMS = 0; // 0x0
+ field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14
field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
@@ -32053,6 +32065,7 @@
}
public class BatteryManager {
+ method public long computeChargeTimeRemaining();
method public int getIntProperty(int);
method public long getLongProperty(int);
method public boolean isCharging();
@@ -41682,7 +41695,10 @@
field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
field public static final java.lang.String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+ field public static final java.lang.String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_bool";
field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
+ field public static final java.lang.String KEY_DATA_RAPID_NOTIFICATION_BOOL = "data_rapid_notification_bool";
+ field public static final java.lang.String KEY_DATA_WARNING_NOTIFICATION_BOOL = "data_warning_notification_bool";
field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
diff --git a/api/system-current.txt b/api/system-current.txt
index 6186a55..b17ced8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -383,12 +383,18 @@
}
public final class StatsManager {
+ method public void addConfig(long, byte[]) throws android.app.StatsManager.StatsUnavailableException;
method public boolean addConfiguration(long, byte[]);
method public byte[] getData(long);
method public byte[] getMetadata();
+ method public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException;
+ method public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException;
+ method public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException;
method public boolean removeConfiguration(long);
+ method public void setBroadcastSubscriber(android.app.PendingIntent, long, long) throws android.app.StatsManager.StatsUnavailableException;
method public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
method public boolean setDataFetchOperation(long, android.app.PendingIntent);
+ method public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException;
field public static final java.lang.String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
field public static final java.lang.String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES";
field public static final java.lang.String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
@@ -398,6 +404,11 @@
field public static final java.lang.String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
}
+ public static class StatsManager.StatsUnavailableException extends android.util.AndroidException {
+ ctor public StatsManager.StatsUnavailableException(java.lang.String);
+ ctor public StatsManager.StatsUnavailableException(java.lang.String, java.lang.Throwable);
+ }
+
public class VrManager {
method public void setAndBindVrCompositor(android.content.ComponentName);
method public void setPersistentVrModeEnabled(boolean);
@@ -1027,11 +1038,10 @@
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 android.os.PersistableBundle getSuspendedPackageAppExtras(java.lang.String);
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 boolean isPackageSuspended(java.lang.String);
+ method public boolean isPackageSuspended(java.lang.String) 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 abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
@@ -1039,7 +1049,6 @@
method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
- method public void setSuspendedPackageAppExtras(java.lang.String, android.os.PersistableBundle);
method public abstract void setUpdateAvailable(java.lang.String, boolean);
method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
@@ -3079,37 +3088,6 @@
method public void onTetheringStarted();
}
- public final class IpSecManager {
- method public void applyTunnelModeTransform(android.net.IpSecManager.IpSecTunnelInterface, int, android.net.IpSecTransform) throws java.io.IOException;
- method public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(java.net.InetAddress, java.net.InetAddress, android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- }
-
- public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
- method public void addAddress(java.net.InetAddress, int) throws java.io.IOException;
- method public void close();
- method public java.lang.String getInterfaceName();
- method public void removeAddress(java.net.InetAddress, int) throws java.io.IOException;
- }
-
- public final class IpSecTransform implements java.lang.AutoCloseable {
- method public void startNattKeepalive(android.net.IpSecTransform.NattKeepaliveCallback, int, android.os.Handler) throws java.io.IOException;
- method public void stopNattKeepalive();
- }
-
- public static class IpSecTransform.Builder {
- method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- }
-
- public static class IpSecTransform.NattKeepaliveCallback {
- ctor public IpSecTransform.NattKeepaliveCallback();
- method public void onError(int);
- method public void onStarted();
- method public void onStopped();
- field public static final int ERROR_HARDWARE_ERROR = 3; // 0x3
- field public static final int ERROR_HARDWARE_UNSUPPORTED = 2; // 0x2
- field public static final int ERROR_INVALID_NETWORK = 1; // 0x1
- }
-
public final class NetworkCapabilities implements android.os.Parcelable {
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
}
@@ -4398,6 +4376,7 @@
method public java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
method public deprecated void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
method public void initRecoveryService(java.lang.String, byte[], byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
+ method public static boolean isRecoverableKeyStoreEnabled(android.content.Context);
method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public void setRecoverySecretTypes(int[]) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
@@ -5074,11 +5053,6 @@
package android.telephony {
- public static final class AccessNetworkConstants.TransportType {
- field public static final int WLAN = 2; // 0x2
- field public static final int WWAN = 1; // 0x1
- }
-
public class CarrierConfigManager {
method public static android.os.PersistableBundle getDefaultConfig();
method public void updateConfigForPhoneId(int, java.lang.String);
@@ -5093,66 +5067,6 @@
field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
- public class NetworkRegistrationState implements android.os.Parcelable {
- ctor public NetworkRegistrationState(int, int, int, int, int, boolean, int[], android.telephony.CellIdentity);
- ctor protected NetworkRegistrationState(android.os.Parcel);
- method public int describeContents();
- method public int getAccessNetworkTechnology();
- method public int[] getAvailableServices();
- method public android.telephony.CellIdentity getCellIdentity();
- method public int getDomain();
- method public int getReasonForDenial();
- method public int getRegState();
- method public int getTransportType();
- method public boolean isEmergencyEnabled();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationState> CREATOR;
- field public static final int DOMAIN_CS = 1; // 0x1
- field public static final int DOMAIN_PS = 2; // 0x2
- field public static final int REG_STATE_DENIED = 3; // 0x3
- field public static final int REG_STATE_HOME = 1; // 0x1
- field public static final int REG_STATE_NOT_REG_NOT_SEARCHING = 0; // 0x0
- field public static final int REG_STATE_NOT_REG_SEARCHING = 2; // 0x2
- field public static final int REG_STATE_ROAMING = 5; // 0x5
- field public static final int REG_STATE_UNKNOWN = 4; // 0x4
- field public static final int SERVICE_TYPE_DATA = 2; // 0x2
- field public static final int SERVICE_TYPE_EMERGENCY = 5; // 0x5
- field public static final int SERVICE_TYPE_SMS = 3; // 0x3
- field public static final int SERVICE_TYPE_VIDEO = 4; // 0x4
- field public static final int SERVICE_TYPE_VOICE = 1; // 0x1
- }
-
- public abstract class NetworkService extends android.app.Service {
- ctor public NetworkService();
- method protected abstract android.telephony.NetworkService.NetworkServiceProvider createNetworkServiceProvider(int);
- field public static final java.lang.String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
- field public static final java.lang.String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
- }
-
- public class NetworkService.NetworkServiceProvider {
- ctor public NetworkService.NetworkServiceProvider(int);
- method public void getNetworkRegistrationState(int, android.telephony.NetworkServiceCallback);
- method public final int getSlotId();
- method public final void notifyNetworkRegistrationStateChanged();
- method protected void onDestroy();
- }
-
- public class NetworkServiceCallback {
- method public void onGetNetworkRegistrationStateComplete(int, android.telephony.NetworkRegistrationState);
- field public static final int RESULT_ERROR_BUSY = 3; // 0x3
- field public static final int RESULT_ERROR_FAILED = 5; // 0x5
- field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
- field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
- field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
- field public static final int RESULT_SUCCESS = 0; // 0x0
- }
-
- public class ServiceState implements android.os.Parcelable {
- method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
- method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
- method public android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
- }
-
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);
@@ -5355,94 +5269,6 @@
}
-package android.telephony.data {
-
- public final class DataCallResponse implements android.os.Parcelable {
- ctor public DataCallResponse(int, int, int, int, java.lang.String, java.lang.String, java.util.List<android.net.LinkAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.lang.String>, int);
- ctor public DataCallResponse(android.os.Parcel);
- method public int describeContents();
- method public int getActive();
- method public java.util.List<android.net.LinkAddress> getAddresses();
- method public int getCallId();
- method public java.util.List<java.net.InetAddress> getDnses();
- method public java.util.List<java.net.InetAddress> getGateways();
- method public java.lang.String getIfname();
- method public int getMtu();
- method public java.util.List<java.lang.String> getPcscfs();
- method public int getStatus();
- method public int getSuggestedRetryTime();
- method public java.lang.String getType();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
- }
-
- public final class DataProfile implements android.os.Parcelable {
- ctor public DataProfile(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, int, int, int, int, boolean, int, java.lang.String, int, int, java.lang.String, java.lang.String, boolean);
- ctor public DataProfile(android.os.Parcel);
- method public int describeContents();
- method public java.lang.String getApn();
- method public int getAuthType();
- method public int getBearerBitmap();
- method public int getMaxConns();
- method public int getMaxConnsTime();
- method public int getMtu();
- method public java.lang.String getMvnoMatchData();
- method public java.lang.String getMvnoType();
- method public java.lang.String getPassword();
- method public int getProfileId();
- method public java.lang.String getProtocol();
- method public java.lang.String getRoamingProtocol();
- method public int getSupportedApnTypesBitmap();
- method public int getType();
- method public java.lang.String getUserName();
- method public int getWaitTime();
- method public boolean isEnabled();
- method public boolean isModemCognitive();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.data.DataProfile> CREATOR;
- field public static final int TYPE_3GPP = 1; // 0x1
- field public static final int TYPE_3GPP2 = 2; // 0x2
- field public static final int TYPE_COMMON = 0; // 0x0
- }
-
- public abstract class DataService extends android.app.Service {
- ctor public DataService();
- method public abstract android.telephony.data.DataService.DataServiceProvider createDataServiceProvider(int);
- field public static final java.lang.String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
- field public static final java.lang.String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
- field public static final int REQUEST_REASON_HANDOVER = 3; // 0x3
- field public static final int REQUEST_REASON_NORMAL = 1; // 0x1
- field public static final int REQUEST_REASON_SHUTDOWN = 2; // 0x2
- }
-
- public class DataService.DataServiceProvider {
- ctor public DataService.DataServiceProvider(int);
- method public void deactivateDataCall(int, int, android.telephony.data.DataServiceCallback);
- method public void getDataCallList(android.telephony.data.DataServiceCallback);
- method public final int getSlotId();
- method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
- method protected void onDestroy();
- method public void setDataProfile(java.util.List<android.telephony.data.DataProfile>, boolean, android.telephony.data.DataServiceCallback);
- method public void setInitialAttachApn(android.telephony.data.DataProfile, boolean, android.telephony.data.DataServiceCallback);
- method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, int, android.net.LinkProperties, android.telephony.data.DataServiceCallback);
- }
-
- public class DataServiceCallback {
- method public void onDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
- method public void onDeactivateDataCallComplete(int);
- method public void onGetDataCallListComplete(int, java.util.List<android.telephony.data.DataCallResponse>);
- method public void onSetDataProfileComplete(int);
- method public void onSetInitialAttachApnComplete(int);
- method public void onSetupDataCallComplete(int, android.telephony.data.DataCallResponse);
- field public static final int RESULT_ERROR_BUSY = 3; // 0x3
- field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
- field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
- field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
- field public static final int RESULT_SUCCESS = 0; // 0x0
- }
-
-}
-
package android.telephony.euicc {
public final class DownloadableSubscription implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 94154c2..ea8e5db 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -492,6 +492,27 @@
method public int getProgramId();
}
+ public final class BufferingParams implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getInitialMarkMs();
+ method public int getResumePlaybackMarkMs();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR;
+ }
+
+ public static class BufferingParams.Builder {
+ ctor public BufferingParams.Builder();
+ ctor public BufferingParams.Builder(android.media.BufferingParams);
+ method public android.media.BufferingParams build();
+ method public android.media.BufferingParams.Builder setInitialMarkMs(int);
+ method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int);
+ }
+
+ public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
+ method public android.media.BufferingParams getBufferingParams();
+ method public void setBufferingParams(android.media.BufferingParams);
+ }
+
public final class PlaybackParams implements android.os.Parcelable {
method public int getAudioStretchMode();
method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -698,6 +719,15 @@
}
+package android.security {
+
+ public class KeyStoreException extends java.lang.Exception {
+ ctor public KeyStoreException(int, java.lang.String);
+ method public int getErrorCode();
+ }
+
+}
+
package android.security.keystore {
public abstract class AttestationUtils {
@@ -707,6 +737,18 @@
field public static final int ID_TYPE_SERIAL = 1; // 0x1
}
+ public static final class KeyGenParameterSpec.Builder {
+ method public android.security.keystore.KeyGenParameterSpec.Builder setUniqueIdIncluded(boolean);
+ }
+
+ public final class KeyProtection implements java.security.KeyStore.ProtectionParameter {
+ method public long getBoundToSpecificSecureUserId();
+ }
+
+ public static final class KeyProtection.Builder {
+ method public android.security.keystore.KeyProtection.Builder setBoundToSpecificSecureUserId(long);
+ }
+
}
package android.service.autofill {
@@ -875,6 +917,10 @@
package android.telephony.mbms {
+ public static class DownloadRequest.Builder {
+ method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String);
+ }
+
public final class FileInfo implements android.os.Parcelable {
ctor public FileInfo(android.net.Uri, java.lang.String);
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 8e46714..e0222d9 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -205,6 +205,8 @@
tests/e2e/Alarm_e2e_test.cpp \
tests/e2e/Attribution_e2e_test.cpp \
tests/e2e/GaugeMetric_e2e_push_test.cpp \
+ tests/e2e/GaugeMetric_e2e_pull_test.cpp \
+ tests/e2e/ValueMetric_pull_e2e_test.cpp \
tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp \
tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp \
tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp \
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index 7d6c47b..e6272ed 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -365,7 +365,8 @@
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
sp<StatsLogProcessor> processor = new StatsLogProcessor(
- uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
+ uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec * NS_PER_SEC,
+ [](const ConfigKey&){});
processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
return processor;
}
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 90ce735..f2443e8 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -72,14 +72,15 @@
StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor,
- const long timeBaseSec,
+ const int64_t timeBaseNs,
const std::function<void(const ConfigKey&)>& sendBroadcast)
: mUidMap(uidMap),
mAnomalyAlarmMonitor(anomalyAlarmMonitor),
mPeriodicAlarmMonitor(periodicAlarmMonitor),
mSendBroadcast(sendBroadcast),
- mTimeBaseSec(timeBaseSec),
- mLastLogTimestamp(0) {
+ mTimeBaseNs(timeBaseNs),
+ mLargestTimestampSeen(0),
+ mLastTimestampSeen(0) {
}
StatsLogProcessor::~StatsLogProcessor() {
@@ -156,18 +157,54 @@
}
void StatsLogProcessor::OnLogEvent(LogEvent* event) {
+ OnLogEvent(event, false);
+}
+
+void StatsLogProcessor::OnLogEvent(LogEvent* event, bool reconnected) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
const int64_t currentTimestampNs = event->GetElapsedTimestampNs();
- if (currentTimestampNs < mLastLogTimestamp) {
- StatsdStats::getInstance().noteLogEventSkipped(
- event->GetTagId(), event->GetElapsedTimestampNs());
- return;
+ if (reconnected && mLastTimestampSeen != 0) {
+ // LogReader tells us the connection has just been reset. Now we need
+ // to enter reconnection state to find the last CP.
+ mInReconnection = true;
+ }
+
+ if (mInReconnection) {
+ // We see the checkpoint
+ if (currentTimestampNs == mLastTimestampSeen) {
+ mInReconnection = false;
+ // Found the CP. ignore this event, and we will start to read from next event.
+ return;
+ }
+ if (currentTimestampNs > mLargestTimestampSeen) {
+ // We see a new log but CP has not been found yet. Give up now.
+ mLogLossCount++;
+ mInReconnection = false;
+ StatsdStats::getInstance().noteLogLost(currentTimestampNs);
+ // Persist the data before we reset. Do we want this?
+ WriteDataToDiskLocked();
+ // We see fresher event before we see the checkpoint. We might have lost data.
+ // The best we can do is to reset.
+ std::vector<ConfigKey> configKeys;
+ for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) {
+ configKeys.push_back(it->first);
+ }
+ resetConfigsLocked(currentTimestampNs, configKeys);
+ } else {
+ // Still in search of the CP. Keep going.
+ return;
+ }
+ }
+
+ mLogCount++;
+ mLastTimestampSeen = currentTimestampNs;
+ if (mLargestTimestampSeen < currentTimestampNs) {
+ mLargestTimestampSeen = currentTimestampNs;
}
resetIfConfigTtlExpiredLocked(currentTimestampNs);
- mLastLogTimestamp = currentTimestampNs;
StatsdStats::getInstance().noteAtomLogged(
event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC);
@@ -210,7 +247,7 @@
const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config) {
VLOG("Updated configuration for key %s", key.ToString().c_str());
sp<MetricsManager> newMetricsManager =
- new MetricsManager(key, config, mTimeBaseSec, (timestampNs - 1) / NS_PER_SEC + 1, mUidMap,
+ new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap,
mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
auto it = mMetricsManagers.find(key);
if (it != mMetricsManagers.end()) {
@@ -339,15 +376,9 @@
(long long)getWallClockNs());
}
-void StatsLogProcessor::resetIfConfigTtlExpiredLocked(const int64_t timestampNs) {
- std::vector<ConfigKey> configKeysTtlExpired;
- for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) {
- if (it->second != nullptr && !it->second->isInTtl(timestampNs)) {
- configKeysTtlExpired.push_back(it->first);
- }
- }
-
- for (const auto& key : configKeysTtlExpired) {
+void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs,
+ const std::vector<ConfigKey>& configs) {
+ for (const auto& key : configs) {
StatsdConfig config;
if (StorageManager::readConfigFromDisk(key, &config)) {
OnConfigUpdatedLocked(timestampNs, key, config);
@@ -362,6 +393,18 @@
}
}
+void StatsLogProcessor::resetIfConfigTtlExpiredLocked(const int64_t timestampNs) {
+ std::vector<ConfigKey> configKeysTtlExpired;
+ for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) {
+ if (it->second != nullptr && !it->second->isInTtl(timestampNs)) {
+ configKeysTtlExpired.push_back(it->first);
+ }
+ }
+ if (configKeysTtlExpired.size() > 0) {
+ resetConfigsLocked(timestampNs, configKeysTtlExpired);
+ }
+}
+
void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
auto it = mMetricsManagers.find(key);
@@ -438,6 +481,10 @@
WriteDataToDiskLocked();
}
+void StatsLogProcessor::informPullAlarmFired(const int64_t timestampNs) {
+ mStatsPullerManager.OnAlarmFired(timestampNs);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 1e82b1e..6efdf8c 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -36,10 +36,13 @@
public:
StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor,
- const long timeBaseSec,
+ const int64_t timeBaseNs,
const std::function<void(const ConfigKey&)>& sendBroadcast);
virtual ~StatsLogProcessor();
+ void OnLogEvent(LogEvent* event, bool reconnectionStarts);
+
+ // for testing only.
void OnLogEvent(LogEvent* event);
void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
@@ -70,6 +73,7 @@
void dumpStates(FILE* out, bool verbose);
+ void informPullAlarmFired(const int64_t timestampNs);
private:
// For testing only.
@@ -121,16 +125,30 @@
// Handler over the isolated uid change event.
void onIsolatedUidChangedEventLocked(const LogEvent& event);
+ void resetConfigsLocked(const int64_t timestampNs, const std::vector<ConfigKey>& configs);
+
// Function used to send a broadcast so that receiver for the config key can call getData
// to retrieve the stored data.
std::function<void(const ConfigKey& key)> mSendBroadcast;
- const long mTimeBaseSec;
+ const int64_t mTimeBaseNs;
- int64_t mLastLogTimestamp;
+ // Largest timestamp of the events that we have processed.
+ int64_t mLargestTimestampSeen = 0;
+
+ int64_t mLastTimestampSeen = 0;
+
+ bool mInReconnection = false;
+
+ // Processed log count
+ uint64_t mLogCount = 0;
+
+ // Log loss detected count
+ int mLogLossCount = 0;
long mLastPullerCacheClearTimeSec = 0;
+ FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
@@ -145,6 +163,12 @@
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
+ FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
+ FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
+ FRIEND_TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
+
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index d3cda63..f7cc00c 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -81,7 +81,7 @@
StatsPuller::SetUidMap(mUidMap);
mConfigManager = new ConfigManager();
mProcessor = new StatsLogProcessor(mUidMap, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
- getElapsedRealtimeSec(), [this](const ConfigKey& key) {
+ getElapsedRealtimeNs(), [this](const ConfigKey& key) {
sp<IStatsCompanionService> sc = getStatsCompanionService();
auto receiver = mConfigManager->GetConfigReceiver(key);
if (sc == nullptr) {
@@ -745,7 +745,7 @@
"Only system uid can call informPollAlarmFired");
}
- mStatsPullerManager.OnAlarmFired();
+ mProcessor->informPullAlarmFired(getElapsedRealtimeNs());
VLOG("StatsService::informPollAlarmFired succeeded");
@@ -780,8 +780,6 @@
}
void StatsService::sayHiToStatsCompanion() {
- // TODO: This method needs to be private. It is temporarily public and unsecured for testing
- // purposes.
sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion != nullptr) {
VLOG("Telling statsCompanion that statsd is ready");
@@ -818,49 +816,44 @@
mConfigManager->Startup();
}
-void StatsService::OnLogEvent(LogEvent* event) {
- mProcessor->OnLogEvent(event);
+void StatsService::OnLogEvent(LogEvent* event, bool reconnectionStarts) {
+ mProcessor->OnLogEvent(event, reconnectionStarts);
}
Status StatsService::getData(int64_t key, vector<uint8_t>* output) {
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
- if (checkCallingPermission(String16(kPermissionDump))) {
- ConfigKey configKey(ipc->getCallingUid(), key);
- mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
- false /* include_current_bucket*/, output);
- return Status::ok();
- } else {
+ if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
+ ConfigKey configKey(ipc->getCallingUid(), key);
+ mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
+ false /* include_current_bucket*/, output);
+ return Status::ok();
}
Status StatsService::getMetadata(vector<uint8_t>* output) {
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::getMetadata with Pid %i, Uid %i", ipc->getCallingPid(),
ipc->getCallingUid());
- if (checkCallingPermission(String16(kPermissionDump))) {
- StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
- return Status::ok();
- } else {
+ if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
+ StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
+ return Status::ok();
}
-Status StatsService::addConfiguration(int64_t key,
- const vector <uint8_t>& config,
- bool* success) {
+Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config) {
+ if (!checkCallingPermission(String16(kPermissionDump))) {
+ return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+ }
IPCThreadState* ipc = IPCThreadState::self();
- if (checkCallingPermission(String16(kPermissionDump))) {
- if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
- *success = true;
- } else {
- *success = false;
- }
+ if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
return Status::ok();
} else {
- *success = false;
- return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+ ALOGE("Could not parse malformatted StatsdConfig");
+ return Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT,
+ "config does not correspond to a StatsdConfig proto");
}
}
@@ -876,80 +869,62 @@
return true;
}
-Status StatsService::removeDataFetchOperation(int64_t key, bool* success) {
- IPCThreadState* ipc = IPCThreadState::self();
- if (checkCallingPermission(String16(kPermissionDump))) {
- ConfigKey configKey(ipc->getCallingUid(), key);
- mConfigManager->RemoveConfigReceiver(configKey);
- *success = true;
- return Status::ok();
- } else {
- *success = false;
+Status StatsService::removeDataFetchOperation(int64_t key) {
+ if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
+ IPCThreadState* ipc = IPCThreadState::self();
+ ConfigKey configKey(ipc->getCallingUid(), key);
+ mConfigManager->RemoveConfigReceiver(configKey);
+ return Status::ok();
}
-Status StatsService::setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender,
- bool* success) {
- IPCThreadState* ipc = IPCThreadState::self();
- if (checkCallingPermission(String16(kPermissionDump))) {
- ConfigKey configKey(ipc->getCallingUid(), key);
- mConfigManager->SetConfigReceiver(configKey, intentSender);
- *success = true;
- return Status::ok();
- } else {
- *success = false;
+Status StatsService::setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender) {
+ if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
+ IPCThreadState* ipc = IPCThreadState::self();
+ ConfigKey configKey(ipc->getCallingUid(), key);
+ mConfigManager->SetConfigReceiver(configKey, intentSender);
+ return Status::ok();
}
-Status StatsService::removeConfiguration(int64_t key, bool* success) {
- IPCThreadState* ipc = IPCThreadState::self();
- if (checkCallingPermission(String16(kPermissionDump))) {
- ConfigKey configKey(ipc->getCallingUid(), key);
- mConfigManager->RemoveConfig(configKey);
- SubscriberReporter::getInstance().removeConfig(configKey);
- *success = true;
- return Status::ok();
- } else {
- *success = false;
+Status StatsService::removeConfiguration(int64_t key) {
+ if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
+ IPCThreadState* ipc = IPCThreadState::self();
+ ConfigKey configKey(ipc->getCallingUid(), key);
+ mConfigManager->RemoveConfig(configKey);
+ SubscriberReporter::getInstance().removeConfig(configKey);
+ return Status::ok();
}
Status StatsService::setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender,
- bool* success) {
+ const sp<android::IBinder>& intentSender) {
VLOG("StatsService::setBroadcastSubscriber called.");
- IPCThreadState* ipc = IPCThreadState::self();
- if (checkCallingPermission(String16(kPermissionDump))) {
- ConfigKey configKey(ipc->getCallingUid(), configId);
- SubscriberReporter::getInstance()
- .setBroadcastSubscriber(configKey, subscriberId, intentSender);
- *success = true;
- return Status::ok();
- } else {
- *success = false;
+ if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
+ IPCThreadState* ipc = IPCThreadState::self();
+ ConfigKey configKey(ipc->getCallingUid(), configId);
+ SubscriberReporter::getInstance()
+ .setBroadcastSubscriber(configKey, subscriberId, intentSender);
+ return Status::ok();
}
Status StatsService::unsetBroadcastSubscriber(int64_t configId,
- int64_t subscriberId,
- bool* success) {
+ int64_t subscriberId) {
VLOG("StatsService::unsetBroadcastSubscriber called.");
- IPCThreadState* ipc = IPCThreadState::self();
- if (checkCallingPermission(String16(kPermissionDump))) {
- ConfigKey configKey(ipc->getCallingUid(), configId);
- SubscriberReporter::getInstance()
- .unsetBroadcastSubscriber(configKey, subscriberId);
- *success = true;
- return Status::ok();
- } else {
- *success = false;
+ if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
+ IPCThreadState* ipc = IPCThreadState::self();
+ ConfigKey configKey(ipc->getCallingUid(), configId);
+ SubscriberReporter::getInstance()
+ .unsetBroadcastSubscriber(configKey, subscriberId);
+ return Status::ok();
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 648e9c5..d502796 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -76,7 +76,7 @@
/**
* Called by LogReader when there's a log event to process.
*/
- virtual void OnLogEvent(LogEvent* event);
+ virtual void OnLogEvent(LogEvent* event, bool reconnectionStarts);
/**
* Binder call for clients to request data for this configuration key.
@@ -94,24 +94,23 @@
* Binder call to let clients send a configuration and indicate they're interested when they
* should requestData for this configuration.
*/
- virtual Status addConfiguration(int64_t key, const vector<uint8_t>& config,
- bool* success) override;
+ virtual Status addConfiguration(int64_t key, const vector<uint8_t>& config) override;
/**
* Binder call to let clients register the data fetch operation for a configuration.
*/
- virtual Status setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender,
- bool* success) override;
+ virtual Status setDataFetchOperation(int64_t key,
+ const sp<android::IBinder>& intentSender) override;
/**
* Binder call to remove the data fetch operation for the specified config key.
*/
- virtual Status removeDataFetchOperation(int64_t key, bool* success) override;
+ virtual Status removeDataFetchOperation(int64_t key) override;
/**
* Binder call to allow clients to remove the specified configuration.
*/
- virtual Status removeConfiguration(int64_t key, bool* success) override;
+ virtual Status removeConfiguration(int64_t key) override;
/**
* Binder call to associate the given config's subscriberId with the given intentSender.
@@ -119,17 +118,13 @@
*/
virtual Status setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender,
- bool* success) override;
+ const sp<android::IBinder>& intentSender) override;
/**
* Binder call to unassociate the given config's subscriberId with any intentSender.
*/
- virtual Status unsetBroadcastSubscriber(int64_t configId, int64_t subscriberId,
- bool* success) override;
+ virtual Status unsetBroadcastSubscriber(int64_t configId, int64_t subscriberId) override;
- // TODO: public for testing since statsd doesn't run when system starts. Change to private
- // later.
/** Inform statsCompanion that statsd is ready. */
virtual void sayHiToStatsCompanion();
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 83d59c0..50ffe17 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -40,8 +40,8 @@
return mPullerManager.PullerForMatcherExists(tagId);
}
- void OnAlarmFired() {
- mPullerManager.OnAlarmFired();
+ void OnAlarmFired(const int64_t currentTimeNs) {
+ mPullerManager.OnAlarmFired(currentTimeNs);
}
virtual bool Pull(const int tagId, const int64_t timesNs,
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 2f0e885..610faad 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -275,11 +275,9 @@
}
}
-void StatsPullerManagerImpl::OnAlarmFired() {
+void StatsPullerManagerImpl::OnAlarmFired(const int64_t currentTimeNs) {
AutoMutex _l(mLock);
- int64_t currentTimeNs = getElapsedRealtimeNs();
-
int64_t minNextPullTimeNs = LONG_MAX;
vector<pair<int, vector<ReceiverInfo*>>> needToPull =
@@ -288,7 +286,7 @@
vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
if (pair.second.size() != 0) {
for (ReceiverInfo& receiverInfo : pair.second) {
- if (receiverInfo.nextPullTimeNs < currentTimeNs) {
+ if (receiverInfo.nextPullTimeNs <= currentTimeNs) {
receivers.push_back(&receiverInfo);
} else {
if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
@@ -311,10 +309,9 @@
receiverPtr->onDataPulled(data);
// we may have just come out of a coma, compute next pull time
receiverInfo->nextPullTimeNs =
- ceil((double_t)(currentTimeNs - receiverInfo->nextPullTimeNs) /
- receiverInfo->intervalNs) *
- receiverInfo->intervalNs +
- receiverInfo->nextPullTimeNs;
+ (currentTimeNs - receiverInfo->nextPullTimeNs) /
+ receiverInfo->intervalNs * receiverInfo->intervalNs +
+ receiverInfo->intervalNs + receiverInfo->nextPullTimeNs;
if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
minNextPullTimeNs = receiverInfo->nextPullTimeNs;
}
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 8c771f3..56d04b4 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -58,7 +58,7 @@
// Verify if we know how to pull for this matcher
bool PullerForMatcherExists(int tagId) const;
- void OnAlarmFired();
+ void OnAlarmFired(const int64_t timeNs);
bool Pull(const int tagId, const int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data);
@@ -90,6 +90,11 @@
void updateAlarmLocked();
int64_t mNextPullTimeNs;
+
+ FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
+ FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
};
} // namespace statsd
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index b589d0d..ee3ed230 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -50,7 +50,7 @@
// const int FIELD_ID_PULLED_ATOM_STATS = 10; // The proto is written in stats_log_util.cpp
const int FIELD_ID_LOGGER_ERROR_STATS = 11;
const int FIELD_ID_PERIODIC_ALARM_STATS = 12;
-const int FIELD_ID_SKIPPED_LOG_EVENT_STATS = 13;
+const int FIELD_ID_LOG_LOSS_STATS = 14;
const int FIELD_ID_ATOM_STATS_TAG = 1;
const int FIELD_ID_ATOM_STATS_COUNT = 2;
@@ -61,9 +61,6 @@
const int FIELD_ID_LOGGER_STATS_TIME = 1;
const int FIELD_ID_LOGGER_STATS_ERROR_CODE = 2;
-const int FIELD_ID_SKIPPED_LOG_EVENT_STATS_TAG = 1;
-const int FIELD_ID_SKIPPED_LOG_EVENT_STATS_TIMESTAMP = 2;
-
const int FIELD_ID_CONFIG_STATS_UID = 1;
const int FIELD_ID_CONFIG_STATS_ID = 2;
const int FIELD_ID_CONFIG_STATS_CREATION = 3;
@@ -182,6 +179,14 @@
noteConfigResetInternalLocked(key);
}
+void StatsdStats::noteLogLost(int64_t timestampNs) {
+ lock_guard<std::mutex> lock(mLock);
+ if (mLogLossTimestampNs.size() == kMaxLoggerErrors) {
+ mLogLossTimestampNs.pop_front();
+ }
+ mLogLossTimestampNs.push_back(timestampNs);
+}
+
void StatsdStats::noteBroadcastSent(const ConfigKey& key) {
noteBroadcastSent(key, getWallClockSec());
}
@@ -350,15 +355,6 @@
mPushedAtomStats[atomId]++;
}
-void StatsdStats::noteLogEventSkipped(int tag, int64_t timestamp) {
- lock_guard<std::mutex> lock(mLock);
- // grows strictly one at a time. so it won't > kMaxSkippedLogEvents
- if (mSkippedLogEvents.size() == kMaxSkippedLogEvents) {
- mSkippedLogEvents.pop_front();
- }
- mSkippedLogEvents.push_back(std::make_pair(tag, timestamp));
-}
-
void StatsdStats::noteLoggerError(int error) {
lock_guard<std::mutex> lock(mLock);
// grows strictly one at a time. so it won't > kMaxLoggerErrors
@@ -381,7 +377,7 @@
mAnomalyAlarmRegisteredStats = 0;
mPeriodicAlarmRegisteredStats = 0;
mLoggerErrors.clear();
- mSkippedLogEvents.clear();
+ mLogLossTimestampNs.clear();
for (auto& config : mConfigStats) {
config.second->broadcast_sent_time_sec.clear();
config.second->data_drop_time_sec.clear();
@@ -395,6 +391,14 @@
}
}
+string buildTimeString(int64_t timeSec) {
+ time_t t = timeSec;
+ struct tm* tm = localtime(&t);
+ char timeBuffer[80];
+ strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm);
+ return string(timeBuffer);
+}
+
void StatsdStats::dumpStats(FILE* out) const {
lock_guard<std::mutex> lock(mLock);
time_t t = mStartTimeSec;
@@ -437,15 +441,19 @@
}
for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) {
- fprintf(out, "\tbroadcast time: %d\n", broadcastTime);
+ fprintf(out, "\tbroadcast time: %s(%lld)\n",
+ buildTimeString(broadcastTime).c_str(), (long long)broadcastTime);
}
for (const auto& dataDropTime : configStats->data_drop_time_sec) {
- fprintf(out, "\tdata drop time: %d\n", dataDropTime);
+ fprintf(out, "\tdata drop time: %s(%lld)\n",
+ buildTimeString(dataDropTime).c_str(), (long long)dataDropTime);
}
for (const auto& dump : configStats->dump_report_stats) {
- fprintf(out, "\tdump report time: %d bytes: %lld\n", dump.first, (long long)dump.second);
+ fprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n",
+ buildTimeString(dump.first).c_str(), (long long)dump.first,
+ (long long)dump.second);
}
for (const auto& stats : pair.second->matcher_stats) {
@@ -503,8 +511,8 @@
strftime(buffer, sizeof(buffer), "%Y-%m-%d %I:%M%p\n", error_tm);
fprintf(out, "Logger error %d at %s\n", error.second, buffer);
}
- for (const auto& skipped : mSkippedLogEvents) {
- fprintf(out, "Log event (%d) skipped at %lld\n", skipped.first, (long long)skipped.second);
+ for (const auto& loss : mLogLossTimestampNs) {
+ fprintf(out, "Log loss detected at %lld (elapsedRealtimeNs)\n", (long long)loss);
}
}
@@ -660,13 +668,9 @@
proto.end(token);
}
- for (const auto& skipped : mSkippedLogEvents) {
- uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SKIPPED_LOG_EVENT_STATS |
- FIELD_COUNT_REPEATED);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_SKIPPED_LOG_EVENT_STATS_TAG, skipped.first);
- proto.write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_LOG_EVENT_STATS_TIMESTAMP,
- (long long)skipped.second);
- proto.end(token);
+ for (const auto& loss : mLogLossTimestampNs) {
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_LOG_LOSS_STATS | FIELD_COUNT_REPEATED,
+ (long long)loss);
}
output->clear();
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 123a703..2cbcca3 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -102,9 +102,7 @@
// The max number of old config stats we keep.
const static int kMaxIceBoxSize = 20;
- const static int kMaxLoggerErrors = 10;
-
- const static int kMaxSkippedLogEvents = 200;
+ const static int kMaxLoggerErrors = 20;
const static int kMaxTimestampCount = 20;
@@ -280,7 +278,7 @@
/**
* Records statsd skipped an event.
*/
- void noteLogEventSkipped(int tag, int64_t timestamp);
+ void noteLogLost(int64_t timestamp);
/**
* Reset the historical stats. Including all stats in icebox, and the tracked stats about
@@ -337,8 +335,8 @@
// Logd errors. Size capped by kMaxLoggerErrors.
std::list<const std::pair<int, int>> mLoggerErrors;
- // Skipped log events.
- std::list<const std::pair<int, int64_t>> mSkippedLogEvents;
+ // Timestamps when we detect log loss after logd reconnect.
+ std::list<int64_t> mLogLossTimestampNs;
// Stores the number of times statsd modified the anomaly alarm registered with
// StatsCompanionService.
diff --git a/cmds/statsd/src/logd/LogListener.h b/cmds/statsd/src/logd/LogListener.h
index 69ca571..f924040 100644
--- a/cmds/statsd/src/logd/LogListener.h
+++ b/cmds/statsd/src/logd/LogListener.h
@@ -33,7 +33,7 @@
LogListener();
virtual ~LogListener();
- virtual void OnLogEvent(LogEvent* msg) = 0;
+ virtual void OnLogEvent(LogEvent* msg, bool reconnectionStarts) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/logd/LogReader.cpp b/cmds/statsd/src/logd/LogReader.cpp
index 0fe896b..26ae6a3 100644
--- a/cmds/statsd/src/logd/LogReader.cpp
+++ b/cmds/statsd/src/logd/LogReader.cpp
@@ -113,7 +113,8 @@
LogEvent event(msg);
// Call the listener
- mListener->OnLogEvent(&event);
+ mListener->OnLogEvent(&event,
+ lineCount == 1 /* indicate whether it's a new connection */);
}
}
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 4ce4768..8ce9ec7 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -116,11 +116,6 @@
ALOGE("Failed to add service");
return -1;
}
-
- // TODO: This line is temporary, since statsd doesn't start up automatically (and therefore
- // the call in StatsService::SystemRunning() won't ever be called right now).
- // TODO: Are you sure? Don't we need to reconnect to the system process if we get restarted?
- // --joeo
service->sayHiToStatsCompanion();
// Start the log reader thread
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index c77e07b..e21392c 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -92,7 +92,7 @@
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
- (long long)mBucketSizeNs, (long long)mStartTimeNs);
+ (long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
CountMetricProducer::~CountMetricProducer() {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 3125fa7..3661b31 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -122,7 +122,7 @@
}
}
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
- (long long)mBucketSizeNs, (long long)mStartTimeNs);
+ (long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
DurationMetricProducer::~DurationMetricProducer() {
@@ -154,13 +154,13 @@
return make_unique<OringDurationTracker>(
mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
- mStartTimeNs, mBucketSizeNs, mConditionSliced,
+ mTimeBaseNs, mBucketSizeNs, mConditionSliced,
mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
case DurationMetric_AggregationType_MAX_SPARSE:
return make_unique<MaxDurationTracker>(
mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
- mStartTimeNs, mBucketSizeNs, mConditionSliced,
+ mTimeBaseNs, mBucketSizeNs, mConditionSliced,
mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
}
}
@@ -650,7 +650,7 @@
void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
const LogEvent& event) {
int64_t eventTimeNs = event.GetElapsedTimestampNs();
- if (eventTimeNs < mStartTimeNs) {
+ if (eventTimeNs < mTimeBaseNs) {
return;
}
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 33ab9aa..2f2679e 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -68,7 +68,7 @@
}
mProto = std::make_unique<ProtoOutputStream>();
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
- (long long)mBucketSizeNs, (long long)mStartTimeNs);
+ (long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
EventMetricProducer::~EventMetricProducer() {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 3c77aae..6886f7c 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -61,9 +61,9 @@
GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId,
- const int64_t startTimeNs,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
shared_ptr<StatsPullerManager> statsPullerManager)
- : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
mStatsPullerManager(statsPullerManager),
mPullTagId(pullTagId),
mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
@@ -110,14 +110,15 @@
}
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
+ flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
mStatsPullerManager->RegisterReceiver(
- mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
+ mPullTagId, this, getCurrentBucketEndTimeNs(), mBucketSizeNs);
}
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
- (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs,
+ (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs,
mConditionSliced);
}
@@ -125,14 +126,14 @@
GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId,
- const int64_t startTimeNs)
- : GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId, startTimeNs,
+ const int64_t timeBaseNs, const int64_t startTimeNs)
+ : GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId, timeBaseNs, startTimeNs,
make_shared<StatsPullerManager>()) {
}
GaugeMetricProducer::~GaugeMetricProducer() {
VLOG("~GaugeMetricProducer() called");
- if (mPullTagId != -1) {
+ if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
}
}
@@ -214,18 +215,20 @@
android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.find(
mTagId) ==
android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.end();
- const int64_t wall_clock_ns = truncateTimestamp ?
- truncateTimestampNsToFiveMinutes(getWallClockNs()) : getWallClockNs();
for (const auto& atom : bucket.mGaugeAtoms) {
- int64_t timestampNs = truncateTimestamp ?
- truncateTimestampNsToFiveMinutes(atom.mTimestamps) : atom.mTimestamps;
+ const int64_t elapsedTimestampNs = truncateTimestamp ?
+ truncateTimestampNsToFiveMinutes(atom.mElapsedTimestamps) :
+ atom.mElapsedTimestamps;
+ const int64_t wallClockNs = truncateTimestamp ?
+ truncateTimestampNsToFiveMinutes(atom.mWallClockTimestampNs) :
+ atom.mWallClockTimestampNs;
protoOutput->write(
FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
- (long long)timestampNs);
+ (long long)elapsedTimestampNs);
protoOutput->write(
FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED |
FIELD_ID_WALL_CLOCK_ATOM_TIMESTAMP,
- (long long)wall_clock_ns);
+ (long long)wallClockNs);
}
}
protoOutput->end(bucketInfoToken);
@@ -241,7 +244,7 @@
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
-void GaugeMetricProducer::pullLocked() {
+void GaugeMetricProducer::pullLocked(const int64_t timestampNs) {
bool triggerPuller = false;
switch(mSamplingType) {
// When the metric wants to do random sampling and there is already one gauge atom for the
@@ -262,7 +265,7 @@
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mStatsPullerManager->Pull(mPullTagId, getElapsedRealtimeNs(), &allData)) {
+ if (!mStatsPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d", mPullTagId);
return;
}
@@ -273,26 +276,26 @@
}
void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
- const int64_t eventTime) {
+ const int64_t eventTimeNs) {
VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
- flushIfNeededLocked(eventTime);
+ flushIfNeededLocked(eventTimeNs);
mCondition = conditionMet;
- if (mPullTagId != -1) {
- pullLocked();
+ if (mPullTagId != -1 && mCondition) {
+ pullLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTime) {
+ const int64_t eventTimeNs) {
VLOG("GaugeMetric %lld onSlicedConditionMayChange overall condition %d", (long long)mMetricId,
overallCondition);
- flushIfNeededLocked(eventTime);
+ flushIfNeededLocked(eventTimeNs);
// If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
// pull for every dimension.
mCondition = overallCondition;
if (mPullTagId != -1) {
- pullLocked();
+ pullLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
@@ -360,7 +363,7 @@
if (hitGuardRailLocked(eventKey)) {
return;
}
- GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs);
+ GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs, getWallClockNs());
(*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
// Anomaly detection on gauge metric only works when there is one numeric
// field specified.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 04b7df9..08765c2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -33,11 +33,12 @@
namespace statsd {
struct GaugeAtom {
- GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t timeNs)
- : mFields(fields), mTimestamps(timeNs) {
+ GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int wallClockNs)
+ : mFields(fields), mElapsedTimestamps(elapsedTimeNs), mWallClockTimestampNs(wallClockNs) {
}
std::shared_ptr<vector<FieldValue>> mFields;
- int64_t mTimestamps;
+ int64_t mElapsedTimestamps;
+ int64_t mWallClockTimestampNs;
};
struct GaugeBucket {
@@ -57,7 +58,7 @@
public:
GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int pullTagId, const int64_t startTimeNs);
+ const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs);
virtual ~GaugeMetricProducer();
@@ -76,7 +77,7 @@
flushCurrentBucketLocked(eventTimeNs);
mCurrentBucketStartTimeNs = eventTimeNs;
if (mPullTagId != -1) {
- pullLocked();
+ pullLocked(eventTimeNs);
}
};
@@ -94,7 +95,8 @@
// for testing
GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int pullTagId, const int64_t startTimeNs,
+ const int pullTagId,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
std::shared_ptr<StatsPullerManager> statsPullerManager);
// Internal interface to handle condition change.
@@ -115,7 +117,7 @@
void flushCurrentBucketLocked(const int64_t& eventTimeNs) override;
- void pullLocked();
+ void pullLocked(const int64_t timestampNs);
int mTagId;
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index bcf0e62..5ff8082 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -27,7 +27,7 @@
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
int64_t eventTimeNs = event.GetElapsedTimestampNs();
// this is old event, maybe statsd restarted?
- if (eventTimeNs < mStartTimeNs) {
+ if (eventTimeNs < mTimeBaseNs) {
return;
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index f931e57..532ecbf 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -40,12 +40,12 @@
// be a no-op.
class MetricProducer : public virtual PackageInfoListener {
public:
- MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t startTimeNs,
+ MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
const int conditionIndex, const sp<ConditionWizard>& wizard)
: mMetricId(metricId),
mConfigKey(key),
- mStartTimeNs(startTimeNs),
- mCurrentBucketStartTimeNs(startTimeNs),
+ mTimeBaseNs(timeBaseNs),
+ mCurrentBucketStartTimeNs(timeBaseNs),
mCurrentBucketNum(0),
mCondition(conditionIndex >= 0 ? false : true),
mConditionSliced(false),
@@ -165,6 +165,11 @@
dropDataLocked(dropTimeNs);
}
+ // For test only.
+ inline int64_t getCurrentBucketNum() const {
+ return mCurrentBucketNum;
+ }
+
protected:
virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
@@ -204,7 +209,7 @@
// Convenience to compute the current bucket's end time, which is always aligned with the
// start time of the metric.
int64_t getCurrentBucketEndTimeNs() const {
- return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
+ return mTimeBaseNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
}
virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
@@ -215,7 +220,7 @@
// The time when this metric producer was first created. The end time for the current bucket
// can be computed from this based on mCurrentBucketNum.
- int64_t mStartTimeNs;
+ int64_t mTimeBaseNs;
// Start time may not be aligned with the start of statsd if there is an app upgrade in the
// middle of a bucket.
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index b7f1bd5..47a1a86 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -54,21 +54,21 @@
const int FIELD_ID_ANNOTATIONS_INT32 = 2;
MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
- const long timeBaseSec, const long currentTimeSec,
+ const int64_t timeBaseNs, const int64_t currentTimeNs,
const sp<UidMap> &uidMap,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor)
: mConfigKey(key), mUidMap(uidMap),
mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
mTtlEndNs(-1),
- mLastReportTimeNs(timeBaseSec * NS_PER_SEC),
+ mLastReportTimeNs(timeBaseNs),
mLastReportWallClockNs(getWallClockNs()) {
// Init the ttl end timestamp.
- refreshTtl(timeBaseSec * NS_PER_SEC);
+ refreshTtl(timeBaseNs);
mConfigValid =
initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, currentTimeSec, mTagIds, mAllAtomMatchers,
+ timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers,
mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers,
mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap,
mTrackerToConditionMap, mNoReportMetricIds);
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 6aa260a..3d2c595 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -37,7 +37,7 @@
class MetricsManager : public PackageInfoListener {
public:
MetricsManager(const ConfigKey& configKey, const StatsdConfig& config,
- const long timeBaseSec, const long currentTimeSec,
+ const int64_t timeBaseNs, const int64_t currentTimeNs,
const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor);
@@ -187,6 +187,11 @@
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
+ FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
+ FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
+ FRIEND_TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 51fac8c..844c728 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -63,9 +63,9 @@
ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId,
- const int64_t startTimeNs,
+ const int64_t timeBaseNs, const int64_t startTimestampNs,
shared_ptr<StatsPullerManager> statsPullerManager)
- : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
mValueField(metric.value_field()),
mStatsPullerManager(statsPullerManager),
mPullTagId(pullTagId),
@@ -105,27 +105,28 @@
}
}
- if (mValueField.child_size()) {
+ if (mValueField.child_size() > 0) {
mField = mValueField.child(0).field();
}
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
// Kicks off the puller immediately.
+ flushIfNeededLocked(startTimestampNs);
if (mPullTagId != -1) {
mStatsPullerManager->RegisterReceiver(
mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
}
VLOG("value metric %lld created. bucket size %lld start_time: %lld",
- (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs);
+ (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
// for testing
ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId,
- const int64_t startTimeNs)
- : ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId, startTimeNs,
+ const int64_t timeBaseNs, const int64_t startTimeNs)
+ : ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId, timeBaseNs, startTimeNs,
make_shared<StatsPullerManager>()) {
}
@@ -198,7 +199,6 @@
VLOG("metric %lld dump report now...", (long long)mMetricId);
mPastBuckets.clear();
- // TODO: Clear mDimensionKeyMap once the report is dumped.
}
void ValueMetricProducer::onConditionChangedLocked(const bool condition,
@@ -237,8 +237,8 @@
// For scheduled pulled data, the effective event time is snap to the nearest
// bucket boundary to make bucket finalize.
int64_t realEventTime = allData.at(0)->GetElapsedTimestampNs();
- int64_t eventTime = mStartTimeNs +
- ((realEventTime - mStartTimeNs) / mBucketSizeNs) * mBucketSizeNs;
+ int64_t eventTime = mTimeBaseNs +
+ ((realEventTime - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
mCondition = false;
for (const auto& data : allData) {
@@ -310,7 +310,7 @@
Interval& interval = mCurrentSlicedBucket[eventKey];
int error = 0;
- const long value = event.GetLong(mField, &error);
+ const int64_t value = event.GetLong(mField, &error);
if (error < 0) {
return;
}
@@ -334,14 +334,16 @@
} else {
interval.sum += value;
}
+ interval.hasValue = true;
interval.startUpdated = false;
} else {
- VLOG("No start for matching end %ld", value);
+ VLOG("No start for matching end %lld", (long long)value);
interval.tainted += 1;
}
}
} else { // for pushed events
interval.sum += value;
+ interval.hasValue = true;
}
long wholeBucketVal = interval.sum;
@@ -357,7 +359,7 @@
void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
- if (currentBucketEndTimeNs > eventTimeNs) {
+ if (eventTimeNs < currentBucketEndTimeNs) {
VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
(long long)(currentBucketEndTimeNs));
return;
@@ -393,10 +395,12 @@
for (const auto& slice : mCurrentSlicedBucket) {
tainted += slice.second.tainted;
tainted += slice.second.startUpdated;
- info.mValue = slice.second.sum;
- // it will auto create new vector of ValuebucketInfo if the key is not found.
- auto& bucketList = mPastBuckets[slice.first];
- bucketList.push_back(info);
+ if (slice.second.hasValue) {
+ info.mValue = slice.second.sum;
+ // it will auto create new vector of ValuebucketInfo if the key is not found.
+ auto& bucketList = mPastBuckets[slice.first];
+ bucketList.push_back(info);
+ }
}
VLOG("%d tainted pairs in the bucket", tainted);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index b5f6429..9c5a56c 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -40,7 +40,7 @@
public:
ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int pullTagId, const int64_t startTimeNs);
+ const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs);
virtual ~ValueMetricProducer();
@@ -115,7 +115,7 @@
// for testing
ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int pullTagId, const int64_t startTimeNs,
+ const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
std::shared_ptr<StatsPullerManager> statsPullerManager);
// tagId for pulled data. -1 if this is not pulled
@@ -127,19 +127,22 @@
typedef struct {
// Pulled data always come in pair of <start, end>. This holds the value
// for start. The diff (end - start) is added to sum.
- long start;
+ int64_t start;
// Whether the start data point is updated
bool startUpdated;
// If end data point comes before the start, record this pair as tainted
// and the value is not added to the running sum.
int tainted;
// Running sum of known pairs in this bucket
- long sum;
+ int64_t sum;
+ // If this dimension has any non-tainted value. If not, don't report the
+ // dimension.
+ bool hasValue;
} Interval;
std::unordered_map<MetricDimensionKey, Interval> mCurrentSlicedBucket;
- std::unordered_map<MetricDimensionKey, long> mCurrentFullBucket;
+ std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 566d34e..811a00e 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -262,7 +262,8 @@
return true;
}
-bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec,
+bool initMetrics(const ConfigKey& key, const StatsdConfig& config,
+ const int64_t timeBaseTimeNs, const int64_t currentTimeNs,
UidMap& uidMap, const unordered_map<int64_t, int>& logTrackerMap,
const unordered_map<int64_t, int>& conditionTrackerMap,
const vector<sp<LogMatchingTracker>>& allAtomMatchers,
@@ -277,8 +278,6 @@
allMetricProducers.reserve(allMetricsCount);
StatsPullerManager statsPullerManager;
- uint64_t startTimeNs = timeBaseSec * NS_PER_SEC;
-
// Build MetricProducers for each metric defined in config.
// build CountMetricProducer
for (int i = 0; i < config.count_metric_size(); i++) {
@@ -314,7 +313,7 @@
}
sp<MetricProducer> countProducer =
- new CountMetricProducer(key, metric, conditionIndex, wizard, startTimeNs);
+ new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
allMetricProducers.push_back(countProducer);
}
@@ -384,7 +383,7 @@
sp<MetricProducer> durationMetric = new DurationMetricProducer(
key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
- trackerIndices[2], nesting, wizard, internalDimensions, startTimeNs);
+ trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs);
allMetricProducers.push_back(durationMetric);
}
@@ -420,7 +419,7 @@
}
sp<MetricProducer> eventMetric =
- new EventMetricProducer(key, metric, conditionIndex, wizard, startTimeNs);
+ new EventMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
allMetricProducers.push_back(eventMetric);
}
@@ -467,7 +466,8 @@
}
sp<MetricProducer> valueProducer = new ValueMetricProducer(key, metric, conditionIndex,
- wizard, pullTagId, startTimeNs);
+ wizard, pullTagId,
+ timeBaseTimeNs, currentTimeNs);
allMetricProducers.push_back(valueProducer);
}
@@ -526,7 +526,7 @@
}
sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
- key, metric, conditionIndex, wizard, pullTagId, startTimeNs);
+ key, metric, conditionIndex, wizard, pullTagId, timeBaseTimeNs, currentTimeNs);
allMetricProducers.push_back(gaugeProducer);
}
for (int i = 0; i < config.no_report_metric_size(); ++i) {
@@ -601,11 +601,11 @@
bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
const sp<AlarmMonitor>& periodicAlarmMonitor,
- const long timeBaseSec, const long currentTimeSec,
+ const int64_t timeBaseNs, const int64_t currentTimeNs,
vector<sp<AlarmTracker>>& allAlarmTrackers) {
unordered_map<int64_t, int> alarmTrackerMap;
- uint64_t startMillis = (uint64_t)timeBaseSec * MS_PER_SEC;
- uint64_t currentTimeMillis = (uint64_t)currentTimeSec * MS_PER_SEC;
+ int64_t startMillis = timeBaseNs / 1000 / 1000;
+ int64_t currentTimeMillis = currentTimeNs / 1000 /1000;
for (int i = 0; i < config.alarm_size(); i++) {
const Alarm& alarm = config.alarm(i);
if (alarm.offset_millis() <= 0) {
@@ -646,8 +646,9 @@
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const long timeBaseSec,
- const long currentTimeSec, set<int>& allTagIds,
+ const sp<AlarmMonitor>& periodicAlarmMonitor,
+ const int64_t timeBaseNs, const int64_t currentTimeNs,
+ set<int>& allTagIds,
vector<sp<LogMatchingTracker>>& allAtomMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
@@ -673,7 +674,8 @@
return false;
}
- if (!initMetrics(key, config, timeBaseSec, uidMap, logTrackerMap, conditionTrackerMap,
+ if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap,
+ logTrackerMap, conditionTrackerMap,
allAtomMatchers, allConditionTrackers, allMetricProducers,
conditionToMetricMap, trackerToMetricMap, metricProducerMap,
noReportMetricIds)) {
@@ -686,7 +688,7 @@
return false;
}
if (!initAlarms(config, key, periodicAlarmMonitor,
- timeBaseSec, currentTimeSec, allPeriodicAlarmTrackers)) {
+ timeBaseNs, currentTimeNs, allPeriodicAlarmTrackers)) {
ALOGE("initAlarms failed");
return false;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 0ebdcf9..d749bf4 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -81,7 +81,9 @@
// the list of MetricProducer index
// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
bool initMetrics(
- const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, UidMap& uidMap,
+ const ConfigKey& key, const StatsdConfig& config,
+ const int64_t timeBaseTimeNs, const int64_t currentTimeNs,
+ UidMap& uidMap,
const std::unordered_map<int64_t, int>& logTrackerMap,
const std::unordered_map<int64_t, int>& conditionTrackerMap,
const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
@@ -96,8 +98,9 @@
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const long timeBaseSec,
- const long currentTimeSec, std::set<int>& allTagIds,
+ const sp<AlarmMonitor>& periodicAlarmMonitor,
+ const int64_t timeBaseNs, const int64_t currentTimeNs,
+ std::set<int>& allTagIds,
std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 36b24c8..eaa7bf1 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -97,9 +97,9 @@
}
message GaugeBucketInfo {
- optional int64 start_bucket_nanos = 1;
+ optional int64 start_bucket_elapsed_nanos = 1;
- optional int64 end_bucket_nanos = 2;
+ optional int64 end_bucket_elapsed_nanos = 2;
repeated Atom atom = 3;
@@ -305,4 +305,6 @@
optional int64 elapsed_timestamp_nanos = 2;
}
repeated SkippedLogEventStats skipped_log_event_stats = 13;
+
+ repeated int64 log_loss_stats = 14;
}
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index fb8877a..91a40e3 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -178,6 +178,128 @@
EXPECT_EQ(2, report.annotation(0).field_int32());
}
+TEST(StatsLogProcessorTest, TestOutOfOrderLogs) {
+ // Setup simple config key corresponding to empty config.
+ sp<UidMap> m = new UidMap();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
+ int broadcastCount = 0;
+ StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+
+ LogEvent event1(0, 1 /*logd timestamp*/, 1001 /*elapsedRealtime*/);
+ event1.init();
+
+ LogEvent event2(0, 2, 1002);
+ event2.init();
+
+ LogEvent event3(0, 3, 1005);
+ event3.init();
+
+ LogEvent event4(0, 4, 1004);
+ event4.init();
+
+ // <----- Reconnection happens
+
+ LogEvent event5(0, 5, 999);
+ event5.init();
+
+ LogEvent event6(0, 6, 2000);
+ event6.init();
+
+ // <----- Reconnection happens
+
+ LogEvent event7(0, 7, 3000);
+ event7.init();
+
+ // first event ever
+ p.OnLogEvent(&event1, true);
+ EXPECT_EQ(1UL, p.mLogCount);
+ EXPECT_EQ(1001LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(1001LL, p.mLastTimestampSeen);
+
+ p.OnLogEvent(&event2, false);
+ EXPECT_EQ(2UL, p.mLogCount);
+ EXPECT_EQ(1002LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(1002LL, p.mLastTimestampSeen);
+
+ p.OnLogEvent(&event3, false);
+ EXPECT_EQ(3UL, p.mLogCount);
+ EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(1005LL, p.mLastTimestampSeen);
+
+ p.OnLogEvent(&event4, false);
+ EXPECT_EQ(4UL, p.mLogCount);
+ EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(1004LL, p.mLastTimestampSeen);
+ EXPECT_FALSE(p.mInReconnection);
+
+ // Reconnect happens, event1 out of buffer. Read event2
+ p.OnLogEvent(&event2, true);
+ EXPECT_EQ(4UL, p.mLogCount);
+ EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(1004LL, p.mLastTimestampSeen);
+ EXPECT_TRUE(p.mInReconnection);
+
+ p.OnLogEvent(&event3, false);
+ EXPECT_EQ(4UL, p.mLogCount);
+ EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(1004LL, p.mLastTimestampSeen);
+ EXPECT_TRUE(p.mInReconnection);
+
+ p.OnLogEvent(&event4, false);
+ EXPECT_EQ(4UL, p.mLogCount);
+ EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(1004LL, p.mLastTimestampSeen);
+ EXPECT_FALSE(p.mInReconnection);
+
+ // Fresh event comes.
+ p.OnLogEvent(&event5, false);
+ EXPECT_EQ(5UL, p.mLogCount);
+ EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(999LL, p.mLastTimestampSeen);
+
+ p.OnLogEvent(&event6, false);
+ EXPECT_EQ(6UL, p.mLogCount);
+ EXPECT_EQ(2000LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(2000LL, p.mLastTimestampSeen);
+
+ // Reconnect happens, read from event4
+ p.OnLogEvent(&event4, true);
+ EXPECT_EQ(6UL, p.mLogCount);
+ EXPECT_EQ(2000LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(2000LL, p.mLastTimestampSeen);
+ EXPECT_TRUE(p.mInReconnection);
+
+ p.OnLogEvent(&event5, false);
+ EXPECT_EQ(6UL, p.mLogCount);
+ EXPECT_EQ(2000LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(2000LL, p.mLastTimestampSeen);
+ EXPECT_TRUE(p.mInReconnection);
+
+ // Before we get out of reconnection state, it reconnects again.
+ p.OnLogEvent(&event5, true);
+ EXPECT_EQ(6UL, p.mLogCount);
+ EXPECT_EQ(2000LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(2000LL, p.mLastTimestampSeen);
+ EXPECT_TRUE(p.mInReconnection);
+
+ p.OnLogEvent(&event6, false);
+ EXPECT_EQ(6UL, p.mLogCount);
+ EXPECT_EQ(2000LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(2000LL, p.mLastTimestampSeen);
+ EXPECT_FALSE(p.mInReconnection);
+ EXPECT_EQ(0, p.mLogLossCount);
+
+ // it reconnects again. All old events are gone. We lose CP.
+ p.OnLogEvent(&event7, true);
+ EXPECT_EQ(7UL, p.mLogCount);
+ EXPECT_EQ(3000LL, p.mLargestTimestampSeen);
+ EXPECT_EQ(3000LL, p.mLastTimestampSeen);
+ EXPECT_EQ(1, p.mLogLossCount);
+ EXPECT_FALSE(p.mInReconnection);
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp b/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
index 73c4e7b8..9ea0b81 100644
--- a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
@@ -51,7 +51,7 @@
int64_t bucketStartTimeNs = 10000000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
EXPECT_EQ(2u, processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers.size());
diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
index 93ecde5..c78d99e7 100644
--- a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
@@ -65,7 +65,7 @@
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
@@ -168,7 +168,7 @@
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
index e924b03..50da9e2 100644
--- a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
@@ -101,7 +101,7 @@
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
@@ -278,7 +278,7 @@
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
@@ -391,7 +391,7 @@
config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec);
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 4dd0da8..4f035be 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -62,7 +62,7 @@
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -204,7 +204,7 @@
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
index bb3ad64..b98dc60 100644
--- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
@@ -68,7 +68,7 @@
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
index eb57d470..0758fd0 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
@@ -90,7 +90,7 @@
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -404,7 +404,7 @@
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -648,7 +648,7 @@
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
index 9729a2e..e74be1e 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
@@ -75,7 +75,7 @@
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -283,7 +283,7 @@
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
@@ -465,7 +465,8 @@
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -666,7 +667,8 @@
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
index 4e2c36e..c32048b 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
@@ -80,7 +80,7 @@
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -363,7 +363,7 @@
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -582,7 +582,7 @@
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
new file mode 100644
index 0000000..9561fcf4
--- /dev/null
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -0,0 +1,396 @@
+// 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.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto temperatureAtomMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = temperatureAtomMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ *config.add_predicate() = screenIsOffPredicate;
+
+ auto gaugeMetric = config.add_gauge_metric();
+ gaugeMetric->set_id(123456);
+ gaugeMetric->set_what(temperatureAtomMatcher.id());
+ gaugeMetric->set_condition(screenIsOffPredicate.id());
+ gaugeMetric->set_sampling_type(sampling_type);
+ gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
+ *gaugeMetric->mutable_dimensions_in_what() =
+ CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
+ gaugeMetric->set_bucket(FIVE_MINUTES);
+
+ return config;
+}
+
+} // namespace
+
+TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
+ auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
+ int64_t baseTimeNs = 10 * NS_PER_SEC;
+ int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(
+ baseTimeNs, configAddedTimeNs, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ int startBucketNum = processor->mMetricsManagers.begin()->second->
+ mAllMetricProducers[0]->getCurrentBucketNum();
+ EXPECT_GT(startBucketNum, (int64_t)0);
+
+ // When creating the config, the gauge metric producer should register the alarm at the
+ // end of the current bucket.
+ EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+ EXPECT_EQ(bucketSizeNs,
+ StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+ second.front().intervalNs);
+ int64_t& nextPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+ second.front().nextPullTimeNs;
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
+
+ auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 55);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ // Pulling alarm arrives on time and reset the sequential pulling alarm.
+ processor->informPullAlarmFired(nextPullTimeNs + 1);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
+
+ auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ configAddedTimeNs + bucketSizeNs + 10);
+ processor->OnLogEvent(screenOnEvent.get());
+
+ screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + bucketSizeNs + 100);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ processor->informPullAlarmFired(nextPullTimeNs + 1);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs,
+ nextPullTimeNs);
+
+ processor->informPullAlarmFired(nextPullTimeNs + 1);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
+
+ screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ configAddedTimeNs + 3 * bucketSizeNs + 2);
+ processor->OnLogEvent(screenOnEvent.get());
+
+ processor->informPullAlarmFired(nextPullTimeNs + 3);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
+
+ screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 5 * bucketSizeNs + 1);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ processor->informPullAlarmFired(nextPullTimeNs + 2);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs);
+
+ processor->informPullAlarmFired(nextPullTimeNs + 2);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+
+ auto data = gaugeMetrics.data(0);
+ EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(2 /* sensor name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+ EXPECT_EQ(6, data.bucket_info_size());
+
+ EXPECT_EQ(1, data.bucket_info(0).atom_size());
+ EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
+
+ EXPECT_EQ(1, data.bucket_info(1).atom_size());
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1,
+ data.bucket_info(1).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
+
+ EXPECT_EQ(1, data.bucket_info(2).atom_size());
+ EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1,
+ data.bucket_info(2).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
+
+ EXPECT_EQ(1, data.bucket_info(3).atom_size());
+ EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1,
+ data.bucket_info(3).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(3).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(3).atom(0).temperature().temperature_dc(), 0);
+
+ EXPECT_EQ(1, data.bucket_info(4).atom_size());
+ EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1,
+ data.bucket_info(4).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(4).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(4).atom(0).temperature().temperature_dc(), 0);
+
+ EXPECT_EQ(1, data.bucket_info(5).atom_size());
+ EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2,
+ data.bucket_info(5).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(5).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(5).atom(0).temperature().temperature_dc(), 0);
+}
+
+TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) {
+ auto config = CreateStatsdConfig(GaugeMetric::ALL_CONDITION_CHANGES);
+ int64_t baseTimeNs = 10 * NS_PER_SEC;
+ int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(
+ baseTimeNs, configAddedTimeNs, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ int startBucketNum = processor->mMetricsManagers.begin()->second->
+ mAllMetricProducers[0]->getCurrentBucketNum();
+ EXPECT_GT(startBucketNum, (int64_t)0);
+
+ auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 55);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ configAddedTimeNs + bucketSizeNs + 10);
+ processor->OnLogEvent(screenOnEvent.get());
+
+ screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + bucketSizeNs + 100);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ configAddedTimeNs + 3 * bucketSizeNs + 2);
+ processor->OnLogEvent(screenOnEvent.get());
+
+ screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 5 * bucketSizeNs + 1);
+ processor->OnLogEvent(screenOffEvent.get());
+ screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ configAddedTimeNs + 5 * bucketSizeNs + 3);
+ processor->OnLogEvent(screenOnEvent.get());
+ screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 5 * bucketSizeNs + 10);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+
+ auto data = gaugeMetrics.data(0);
+ EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(2 /* sensor name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+ EXPECT_EQ(3, data.bucket_info_size());
+
+ EXPECT_EQ(1, data.bucket_info(0).atom_size());
+ EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
+
+ EXPECT_EQ(1, data.bucket_info(1).atom_size());
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100,
+ data.bucket_info(1).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
+
+ EXPECT_EQ(2, data.bucket_info(2).atom_size());
+ EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1,
+ data.bucket_info(2).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10,
+ data.bucket_info(2).elapsed_timestamp_nanos(1));
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_FALSE(data.bucket_info(2).atom(1).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(2).atom(1).temperature().temperature_dc(), 0);
+}
+
+
+TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
+ auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
+ int64_t baseTimeNs = 10 * NS_PER_SEC;
+ int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(
+ baseTimeNs, configAddedTimeNs, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ int startBucketNum = processor->mMetricsManagers.begin()->second->
+ mAllMetricProducers[0]->getCurrentBucketNum();
+ EXPECT_GT(startBucketNum, (int64_t)0);
+
+ // When creating the config, the gauge metric producer should register the alarm at the
+ // end of the current bucket.
+ EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+ EXPECT_EQ(bucketSizeNs,
+ StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+ second.front().intervalNs);
+ int64_t& nextPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+ second.front().nextPullTimeNs;
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
+
+ auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 55);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ configAddedTimeNs + bucketSizeNs + 10);
+ processor->OnLogEvent(screenOnEvent.get());
+
+ // Pulling alarm arrives one bucket size late.
+ processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
+
+ screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 3 * bucketSizeNs + 11);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ // Pulling alarm arrives more than one bucket size late.
+ processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs + 12);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+
+ auto data = gaugeMetrics.data(0);
+ EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(2 /* sensor name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+ EXPECT_EQ(3, data.bucket_info_size());
+
+ EXPECT_EQ(1, data.bucket_info(0).atom_size());
+ EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
+
+ EXPECT_EQ(1, data.bucket_info(1).atom_size());
+ EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11,
+ data.bucket_info(1).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
+
+ EXPECT_EQ(1, data.bucket_info(2).atom_size());
+ EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12,
+ data.bucket_info(2).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+ EXPECT_FALSE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
+ EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
+
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 18a0485..d79cb336 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -96,7 +96,8 @@
TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -169,9 +170,11 @@
EXPECT_EQ(2, data.bucket_info(0).atom_size());
EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(2, data.bucket_info(0).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_nanos());
- EXPECT_EQ(AppStartOccurred::HOT, data.bucket_info(0).atom(0).app_start_occurred().type());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(AppStartOccurred::HOT,
+ data.bucket_info(0).atom(0).app_start_occurred().type());
EXPECT_EQ("activity_name2",
data.bucket_info(0).atom(0).app_start_occurred().activity_name());
EXPECT_EQ(102L,
@@ -186,8 +189,10 @@
EXPECT_EQ(1, data.bucket_info(1).atom_size());
EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(1).start_bucket_nanos());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(1).end_bucket_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+ data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(1).end_bucket_elapsed_nanos());
EXPECT_EQ(AppStartOccurred::WARM,
data.bucket_info(1).atom(0).app_start_occurred().type());
EXPECT_EQ("activity_name4",
@@ -199,9 +204,9 @@
EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
EXPECT_EQ(2, data.bucket_info(2).wall_clock_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(2).start_bucket_nanos());
+ data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
- data.bucket_info(2).end_bucket_nanos());
+ data.bucket_info(2).end_bucket_elapsed_nanos());
EXPECT_EQ(AppStartOccurred::COLD,
data.bucket_info(2).atom(0).app_start_occurred().type());
EXPECT_EQ("activity_name5",
@@ -218,9 +223,11 @@
EXPECT_EQ(1, data.bucket_info(0).atom_size());
EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_nanos());
- EXPECT_EQ(AppStartOccurred::HOT, data.bucket_info(0).atom(0).app_start_occurred().type());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(AppStartOccurred::HOT,
+ data.bucket_info(0).atom(0).app_start_occurred().type());
EXPECT_EQ("activity_name2",
data.bucket_info(0).atom(0).app_start_occurred().activity_name());
EXPECT_EQ(102L,
@@ -229,8 +236,10 @@
EXPECT_EQ(1, data.bucket_info(1).atom_size());
EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(1).start_bucket_nanos());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(1).end_bucket_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+ data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(1).end_bucket_elapsed_nanos());
EXPECT_EQ(AppStartOccurred::WARM,
data.bucket_info(1).atom(0).app_start_occurred().type());
EXPECT_EQ("activity_name4",
@@ -242,9 +251,9 @@
EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
EXPECT_EQ(1, data.bucket_info(2).wall_clock_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(2).start_bucket_nanos());
+ data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
- data.bucket_info(2).end_bucket_nanos());
+ data.bucket_info(2).end_bucket_elapsed_nanos());
EXPECT_EQ(AppStartOccurred::COLD,
data.bucket_info(2).atom(0).app_start_occurred().type());
EXPECT_EQ("activity_name5",
@@ -264,8 +273,10 @@
EXPECT_EQ(1, data.bucket_info(0).atom_size());
EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_nanos());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type());
EXPECT_EQ("activity_name7",
data.bucket_info(0).atom(0).app_start_occurred().activity_name());
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 1952a6f..4ace382 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -107,7 +107,7 @@
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -224,7 +224,8 @@
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index d4892ed..5499ee3 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -35,7 +35,7 @@
config.SerializeToString(&str);
std::vector<uint8_t> configAsVec(str.begin(), str.end());
bool success;
- service.addConfiguration(kConfigKey, configAsVec, &success);
+ service.addConfiguration(kConfigKey, configAsVec);
}
ConfigMetricsReport GetReports(StatsService& service) {
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
new file mode 100644
index 0000000..62b6fcc
--- /dev/null
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -0,0 +1,260 @@
+// 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.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfig() {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto temperatureAtomMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = temperatureAtomMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ *config.add_predicate() = screenIsOffPredicate;
+
+ auto valueMetric = config.add_value_metric();
+ valueMetric->set_id(123456);
+ valueMetric->set_what(temperatureAtomMatcher.id());
+ valueMetric->set_condition(screenIsOffPredicate.id());
+ *valueMetric->mutable_value_field() =
+ CreateDimensions(android::util::TEMPERATURE, {3/* temperature degree field */ });
+ *valueMetric->mutable_dimensions_in_what() =
+ CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
+ valueMetric->set_bucket(FIVE_MINUTES);
+
+ return config;
+}
+
+} // namespace
+
+TEST(ValueMetricE2eTest, TestPulledEvents) {
+ auto config = CreateStatsdConfig();
+ int64_t baseTimeNs = 10 * NS_PER_SEC;
+ int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(
+ baseTimeNs, configAddedTimeNs, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ int startBucketNum = processor->mMetricsManagers.begin()->second->
+ mAllMetricProducers[0]->getCurrentBucketNum();
+ EXPECT_GT(startBucketNum, (int64_t)0);
+
+ // When creating the config, the gauge metric producer should register the alarm at the
+ // end of the current bucket.
+ EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+ EXPECT_EQ(bucketSizeNs,
+ StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+ second.front().intervalNs);
+ int64_t& expectedPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+ second.front().nextPullTimeNs;
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
+
+ auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 55);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ configAddedTimeNs + 65);
+ processor->OnLogEvent(screenOnEvent.get());
+
+ screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 75);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ // Pulling alarm arrives on time and reset the sequential pulling alarm.
+ processor->informPullAlarmFired(expectedPullTimeNs + 1);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
+
+ processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+ screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ configAddedTimeNs + 2 * bucketSizeNs + 15);
+ processor->OnLogEvent(screenOnEvent.get());
+
+ processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+ processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+ screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 4 * bucketSizeNs + 11);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+ processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::ValueMetricDataWrapper valueMetrics;
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
+ EXPECT_GT((int)valueMetrics.data_size(), 1);
+
+ auto data = valueMetrics.data(0);
+ EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(2 /* sensor name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+ EXPECT_EQ(5, data.bucket_info_size());
+
+ EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_TRUE(data.bucket_info(0).has_value());
+
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+ EXPECT_TRUE(data.bucket_info(1).has_value());
+
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+ EXPECT_TRUE(data.bucket_info(2).has_value());
+
+ EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
+ EXPECT_TRUE(data.bucket_info(3).has_value());
+
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
+ EXPECT_TRUE(data.bucket_info(4).has_value());
+}
+
+TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
+ auto config = CreateStatsdConfig();
+ int64_t baseTimeNs = 10 * NS_PER_SEC;
+ int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(
+ baseTimeNs, configAddedTimeNs, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ int startBucketNum = processor->mMetricsManagers.begin()->second->
+ mAllMetricProducers[0]->getCurrentBucketNum();
+ EXPECT_GT(startBucketNum, (int64_t)0);
+
+ // When creating the config, the gauge metric producer should register the alarm at the
+ // end of the current bucket.
+ EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+ EXPECT_EQ(bucketSizeNs,
+ StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+ second.front().intervalNs);
+ int64_t& expectedPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+ second.front().nextPullTimeNs;
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
+
+ // Screen off/on/off events.
+ auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 55);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ configAddedTimeNs + 65);
+ processor->OnLogEvent(screenOnEvent.get());
+
+ screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 75);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ // Pulling alarm arrives late by 2 buckets and 1 ns.
+ processor->informPullAlarmFired(expectedPullTimeNs + 2 * bucketSizeNs + 1);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
+
+ screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ configAddedTimeNs + 4 * bucketSizeNs + 65);
+ processor->OnLogEvent(screenOnEvent.get());
+
+ // Pulling alarm arrives late by one bucket size + 21ns.
+ processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
+
+ screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+ configAddedTimeNs + 6 * bucketSizeNs + 31);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 8 * bucketSizeNs, expectedPullTimeNs);
+
+ processor->informPullAlarmFired(expectedPullTimeNs + 1);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 9 * bucketSizeNs, expectedPullTimeNs);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::ValueMetricDataWrapper valueMetrics;
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
+ EXPECT_GT((int)valueMetrics.data_size(), 1);
+
+ auto data = valueMetrics.data(0);
+ EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(2 /* sensor name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+ EXPECT_EQ(3, data.bucket_info_size());
+
+ EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_TRUE(data.bucket_info(0).has_value());
+
+ EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+ EXPECT_TRUE(data.bucket_info(1).has_value());
+
+ EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+ EXPECT_TRUE(data.bucket_info(2).has_value());
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index f2d47c7..55bf4be 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -121,7 +121,7 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
@@ -154,7 +154,7 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
@@ -188,7 +188,7 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
@@ -231,7 +231,7 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
@@ -256,7 +256,7 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
@@ -284,7 +284,7 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index a1f865d..698ce72 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -67,7 +67,7 @@
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- tagId, bucketStartTimeNs, pullerManager);
+ tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
gaugeProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
@@ -144,7 +144,7 @@
make_shared<StrictMock<MockStatsPullerManager>>();
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-1 /* -1 means no pulling */, bucketStartTimeNs,
- pullerManager);
+ bucketStartTimeNs, pullerManager);
gaugeProducer.setBucketSize(60 * NS_PER_SEC);
sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
@@ -228,7 +228,7 @@
}));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- tagId, bucketStartTimeNs, pullerManager);
+ tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
gaugeProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
@@ -297,7 +297,7 @@
}));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId,
- bucketStartTimeNs, pullerManager);
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
gaugeProducer.setBucketSize(60 * NS_PER_SEC);
gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -389,7 +389,7 @@
}));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
- pullerManager);
+ bucketStartTimeNs, pullerManager);
gaugeProducer.setBucketSize(60 * NS_PER_SEC);
gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
@@ -433,7 +433,7 @@
gaugeFieldMatcher->set_field(tagId);
gaugeFieldMatcher->add_child()->set_field(2);
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- tagId, bucketStartTimeNs, pullerManager);
+ tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
gaugeProducer.setBucketSize(60 * NS_PER_SEC);
Alert alert;
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index a2bb734..81d8892 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -68,7 +68,7 @@
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- tagId, bucketStartTimeNs, pullerManager);
+ tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
@@ -90,8 +90,7 @@
EXPECT_EQ(0, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
EXPECT_EQ(11, curInterval.start);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
allData.clear();
event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -108,7 +107,7 @@
EXPECT_EQ(0, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
allData.clear();
@@ -125,7 +124,7 @@
EXPECT_EQ(0, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValue);
}
@@ -169,7 +168,7 @@
}));
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
- pullerManager);
+ bucketStartTimeNs, pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -222,7 +221,7 @@
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
- pullerManager);
+ bucketStartTimeNs, pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -278,7 +277,7 @@
return true;
}));
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs,
- pullerManager);
+ bucketStartTimeNs, pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
@@ -321,7 +320,7 @@
make_shared<StrictMock<MockStatsPullerManager>>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
- pullerManager);
+ bucketStartTimeNs, pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -369,7 +368,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- -1 /*not pulled*/, bucketStartTimeNs);
+ -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs);
valueProducer.setBucketSize(60 * NS_PER_SEC);
sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
@@ -448,7 +447,7 @@
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- tagId, bucketStartTimeNs, pullerManager);
+ tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
@@ -464,15 +463,13 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- valueProducer.setBucketSize(60 * NS_PER_SEC);
// startUpdated:true tainted:0 sum:0 start:11
EXPECT_EQ(true, curInterval.startUpdated);
EXPECT_EQ(0, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
EXPECT_EQ(11, curInterval.start);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// pull 2 at correct time
allData.clear();
@@ -490,7 +487,7 @@
EXPECT_EQ(0, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
// pull 3 come late.
@@ -512,11 +509,8 @@
EXPECT_EQ(36, curInterval.start);
EXPECT_EQ(0, curInterval.sum);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(4UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
- EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second[1].mValue);
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[2].mValue);
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[3].mValue);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
}
/*
@@ -562,7 +556,7 @@
}));
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
- pullerManager);
+ bucketStartTimeNs, pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -582,9 +576,7 @@
EXPECT_EQ(false, curInterval.startUpdated);
EXPECT_EQ(1, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// Now the alarm is delivered.
// since the condition turned to off before this pull finish, it has no effect
@@ -601,9 +593,7 @@
EXPECT_EQ(false, curInterval.startUpdated);
EXPECT_EQ(1, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
}
/*
@@ -660,7 +650,7 @@
}));
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
- pullerManager);
+ bucketStartTimeNs, pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -680,9 +670,7 @@
EXPECT_EQ(false, curInterval.startUpdated);
EXPECT_EQ(1, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// condition changed to true again, before the pull alarm is delivered
valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25);
@@ -691,9 +679,7 @@
EXPECT_EQ(130, curInterval.start);
EXPECT_EQ(1, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// Now the alarm is delivered, but it is considered late, it has no effect
vector<shared_ptr<LogEvent>> allData;
@@ -710,9 +696,7 @@
EXPECT_EQ(130, curInterval.start);
EXPECT_EQ(1, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
}
/*
@@ -758,7 +742,7 @@
}));
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
- pullerManager);
+ bucketStartTimeNs, pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -779,9 +763,7 @@
EXPECT_EQ(false, curInterval.startUpdated);
EXPECT_EQ(1, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// Alarm is delivered in time, but the pull is very slow, and pullers are called in order,
// so this one comes even later
@@ -798,9 +780,7 @@
EXPECT_EQ(false, curInterval.startUpdated);
EXPECT_EQ(1, curInterval.tainted);
EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
}
} // namespace statsd
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 649c399..1264909 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -18,6 +18,7 @@
namespace os {
namespace statsd {
+
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
@@ -26,6 +27,10 @@
return atom_matcher;
}
+AtomMatcher CreateTemperatureAtomMatcher() {
+ return CreateSimpleAtomMatcher("TemperatureMatcher", android::util::TEMPERATURE);
+}
+
AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name,
ScheduledJobStateChanged::State state) {
AtomMatcher atom_matcher;
@@ -444,8 +449,8 @@
return logEvent;
}
-sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
- const ConfigKey& key) {
+sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
+ const StatsdConfig& config, const ConfigKey& key) {
sp<UidMap> uidMap = new UidMap();
sp<AlarmMonitor> anomalyAlarmMonitor =
new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
@@ -454,8 +459,8 @@
new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
[](const sp<IStatsCompanionService>&){});
sp<StatsLogProcessor> processor = new StatsLogProcessor(
- uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
- processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
+ uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, [](const ConfigKey&){});
+ processor->OnConfigUpdated(currentTimeNs, key, config);
return processor;
}
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 1ac630c..6ecca46 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -28,6 +28,9 @@
// Create AtomMatcher proto to simply match a specific atom type.
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
+// Create AtomMatcher proto for temperature atom.
+AtomMatcher CreateTemperatureAtomMatcher();
+
// Create AtomMatcher proto for scheduled job state changed.
AtomMatcher CreateScheduledJobStateChangedAtomMatcher();
@@ -172,8 +175,9 @@
AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
// Create a statsd log event processor upon the start time in seconds, config and key.
-sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
- const ConfigKey& key);
+sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs,
+ const int64_t currentTimeNs,
+ const StatsdConfig& config, const ConfigKey& key);
// Util function to sort the log events by timestamp.
void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index 0e6c933..2b7da6a 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -19,6 +19,7 @@
import android.app.PendingIntent;
import android.app.IntentService;
import android.app.StatsManager;
+import android.app.StatsManager.StatsUnavailableException;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -171,12 +172,16 @@
return;
}
if (mStatsManager != null) {
- byte[] data = mStatsManager.getData(CONFIG_ID);
- if (data != null) {
- displayData(data);
- } else {
- mReportText.setText("Failed!");
+ try {
+ byte[] data = mStatsManager.getReports(CONFIG_ID);
+ if (data != null) {
+ displayData(data);
+ return;
+ }
+ } catch (StatsUnavailableException e) {
+ Log.e(TAG, "Failed to get data from statsd", e);
}
+ mReportText.setText("Failed!");
}
}
});
@@ -194,10 +199,11 @@
byte[] config = new byte[inputStream.available()];
inputStream.read(config);
if (mStatsManager != null) {
- if (mStatsManager.addConfiguration(CONFIG_ID, config)) {
+ try {
+ mStatsManager.addConfig(CONFIG_ID, config);
Toast.makeText(
MainActivity.this, "Config pushed", Toast.LENGTH_LONG).show();
- } else {
+ } catch (StatsUnavailableException | IllegalArgumentException e) {
Toast.makeText(MainActivity.this, "Config push FAILED!",
Toast.LENGTH_LONG).show();
}
@@ -218,11 +224,12 @@
return;
}
if (mStatsManager != null) {
- if (mStatsManager.setDataFetchOperation(CONFIG_ID, pi)) {
+ try {
+ mStatsManager.setFetchReportsOperation(pi, CONFIG_ID);
Toast.makeText(MainActivity.this,
"Receiver specified to pending intent", Toast.LENGTH_LONG)
.show();
- } else {
+ } catch (StatsUnavailableException e) {
Toast.makeText(MainActivity.this, "Statsd did not set receiver",
Toast.LENGTH_LONG)
.show();
@@ -241,10 +248,11 @@
return;
}
if (mStatsManager != null) {
- if (mStatsManager.setDataFetchOperation(CONFIG_ID, null)) {
+ try {
+ mStatsManager.setFetchReportsOperation(null, CONFIG_ID);
Toast.makeText(MainActivity.this, "Receiver remove", Toast.LENGTH_LONG)
.show();
- } else {
+ } catch (StatsUnavailableException e) {
Toast.makeText(MainActivity.this, "Statsd did not remove receiver",
Toast.LENGTH_LONG)
.show();
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
index bed4d98..769f78c 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
@@ -355,7 +355,13 @@
return null;
}
if (mStatsManager != null) {
- byte[] data = mStatsManager.getMetadata();
+ byte[] data;
+ try {
+ data = mStatsManager.getStatsMetadata();
+ } catch (StatsManager.StatsUnavailableException e) {
+ Log.e(TAG, "Failed to get data from statsd", e);
+ return null;
+ }
if (data != null) {
StatsdStatsReport report = null;
boolean good = false;
@@ -375,7 +381,13 @@
return null;
}
if (mStatsManager != null) {
- byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_ID);
+ byte[] data;
+ try {
+ data = mStatsManager.getReports(ConfigFactory.CONFIG_ID);
+ } catch (StatsManager.StatsUnavailableException e) {
+ Log.e(TAG, "Failed to get data from statsd", e);
+ return null;
+ }
if (data != null) {
ConfigMetricsReportList reports = null;
try {
@@ -563,10 +575,11 @@
// TODO: Clear all configs instead of specific ones.
if (mStatsManager != null) {
if (mStarted) {
- if (!mStatsManager.removeConfiguration(ConfigFactory.CONFIG_ID)) {
+ try {
+ mStatsManager.removeConfig(ConfigFactory.CONFIG_ID);
Log.d(TAG, "Removed loadtest statsd configs.");
- } else {
- Log.d(TAG, "Failed to remove loadtest configs.");
+ } catch (StatsManager.StatsUnavailableException e) {
+ Log.e(TAG, "Failed to remove loadtest configs.", e);
}
}
}
@@ -574,12 +587,13 @@
private boolean setConfig(ConfigFactory.ConfigMetadata configData) {
if (mStatsManager != null) {
- if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_ID, configData.bytes)) {
+ try {
+ mStatsManager.addConfig(ConfigFactory.CONFIG_ID, configData.bytes);
mNumMetrics = configData.numMetrics;
Log.d(TAG, "Config pushed to statsd");
return true;
- } else {
- Log.d(TAG, "Failed to push config to statsd");
+ } catch (StatsManager.StatsUnavailableException | IllegalArgumentException e) {
+ Log.e(TAG, "Failed to push config to statsd", e);
}
}
return false;
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 87fb998..7177336 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -32326,7 +32326,7 @@
HSPLandroid/view/IWindowSession$Stub$Proxy;->getDisplayFrame(Landroid/view/IWindow;Landroid/graphics/Rect;)V
HSPLandroid/view/IWindowSession$Stub$Proxy;->getInTouchMode()Z
HSPLandroid/view/IWindowSession$Stub$Proxy;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V
-HSPLandroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
+HSPLandroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
HSPLandroid/view/IWindowSession$Stub$Proxy;->setInsets(Landroid/view/IWindow;ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Region;)V
HSPLandroid/view/IWindowSession$Stub$Proxy;->setTransparentRegion(Landroid/view/IWindow;Landroid/graphics/Region;)V
HSPLandroid/view/IWindowSession$Stub$Proxy;->setWallpaperPosition(Landroid/os/IBinder;FFFF)V
@@ -32349,7 +32349,7 @@
HSPLandroid/view/IWindowSession;->pokeDrawLock(Landroid/os/IBinder;)V
HSPLandroid/view/IWindowSession;->prepareDrag(Landroid/view/IWindow;IIILandroid/view/Surface;)Landroid/os/IBinder;
HSPLandroid/view/IWindowSession;->prepareToReplaceWindows(Landroid/os/IBinder;Z)V
-HSPLandroid/view/IWindowSession;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
+HSPLandroid/view/IWindowSession;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
HSPLandroid/view/IWindowSession;->remove(Landroid/view/IWindow;)V
HSPLandroid/view/IWindowSession;->reportDropResult(Landroid/view/IWindow;Z)V
HSPLandroid/view/IWindowSession;->sendWallpaperCommand(Landroid/os/IBinder;Ljava/lang/String;IIILandroid/os/Bundle;Z)Landroid/os/Bundle;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 73d1102..acd4425 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2,8 +2,19 @@
Landroid/accounts/IAccountAuthenticatorResponse$Stub;-><init>()V
Landroid/accounts/IAccountManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accounts/IAccountManager;
Landroid/accounts/IAccountManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/animation/KeyframeSet;-><init>([Landroid/animation/Keyframe;)V
+Landroid/animation/KeyframeSet;->ofFloat([F)Landroid/animation/KeyframeSet;
+Landroid/animation/KeyframeSet;->ofInt([I)Landroid/animation/KeyframeSet;
+Landroid/animation/KeyframeSet;->ofKeyframe([Landroid/animation/Keyframe;)Landroid/animation/KeyframeSet;
+Landroid/animation/KeyframeSet;->ofObject([Ljava/lang/Object;)Landroid/animation/KeyframeSet;
Landroid/animation/LayoutTransition;->cancel(I)V
Landroid/animation/LayoutTransition;->cancel()V
+Landroid/animation/PropertyValuesHolder$FloatPropertyValuesHolder;-><init>(Landroid/util/Property;[F)V
+Landroid/animation/PropertyValuesHolder$FloatPropertyValuesHolder;-><init>(Ljava/lang/String;[F)V
+Landroid/animation/PropertyValuesHolder$IntPropertyValuesHolder;-><init>(Landroid/util/Property;[I)V
+Landroid/animation/PropertyValuesHolder$IntPropertyValuesHolder;-><init>(Ljava/lang/String;[I)V
+Landroid/animation/PropertyValuesHolder$MultiFloatValuesHolder;-><init>(Ljava/lang/String;Landroid/animation/TypeConverter;Landroid/animation/TypeEvaluator;[Ljava/lang/Object;)V
+Landroid/animation/PropertyValuesHolder$MultiIntValuesHolder;-><init>(Ljava/lang/String;Landroid/animation/TypeConverter;Landroid/animation/TypeEvaluator;[Ljava/lang/Object;)V
Landroid/animation/ValueAnimator;->animateValue(F)V
Landroid/animation/ValueAnimator;->sDurationScale:F
Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions;
@@ -51,6 +62,7 @@
Landroid/app/Activity;->mWindow:Landroid/view/Window;
Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager;
Landroid/app/ActivityOptions;->makeMultiThumbFutureAspectScaleAnimation(Landroid/content/Context;Landroid/os/Handler;Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/app/ActivityOptions$OnAnimationStartedListener;Z)Landroid/app/ActivityOptions;
+Landroid/app/ActivityOptions;->startSharedElementAnimation(Landroid/view/Window;[Landroid/util/Pair;)Landroid/app/ActivityOptions;
Landroid/app/Activity;->setDisablePreviewScreenshots(Z)V
Landroid/app/Activity;->setPersistent(Z)V
Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo;
@@ -124,6 +136,7 @@
Landroid/app/ActivityThread;->mServices:Landroid/util/ArrayMap;
Landroid/app/ActivityThread;->performNewIntents(Landroid/os/IBinder;Ljava/util/List;Z)V
Landroid/app/ActivityThread;->performStopActivity(Landroid/os/IBinder;ZLjava/lang/String;)V
+Landroid/app/ActivityThread;->printRow(Ljava/io/PrintWriter;Ljava/lang/String;[Ljava/lang/Object;)V
Landroid/app/ActivityThread$ProviderClientRecord;->mHolder:Landroid/app/ContentProviderHolder;
Landroid/app/ActivityThread$ProviderClientRecord;->mLocalProvider:Landroid/content/ContentProvider;
Landroid/app/ActivityThread$ProviderClientRecord;->mProvider:Landroid/content/IContentProvider;
@@ -136,6 +149,7 @@
Landroid/app/ActivityThread$ServiceArgsData;->token:Landroid/os/IBinder;
Landroid/app/ActivityThread;->sPackageManager:Landroid/content/pm/IPackageManager;
Landroid/app/ActivityThread;->startActivityNow(Landroid/app/Activity;Ljava/lang/String;Landroid/content/Intent;Landroid/content/pm/ActivityInfo;Landroid/os/IBinder;Landroid/os/Bundle;Landroid/app/Activity$NonConfigurationInstances;)Landroid/app/Activity;
+Landroid/app/admin/DevicePolicyManager;->getProfileOwnerAsUser(I)Landroid/content/ComponentName;
Landroid/app/admin/DevicePolicyManager;->getTrustAgentConfiguration(Landroid/content/ComponentName;Landroid/content/ComponentName;I)Ljava/util/List;
Landroid/app/admin/DevicePolicyManager;->packageHasActiveAdmins(Ljava/lang/String;I)Z
Landroid/app/admin/DevicePolicyManager;->setActiveAdmin(Landroid/content/ComponentName;ZI)V
@@ -145,6 +159,7 @@
Landroid/app/admin/IDevicePolicyManager$Stub;->TRANSACTION_packageHasActiveAdmins:I
Landroid/app/admin/IDevicePolicyManager$Stub;->TRANSACTION_removeActiveAdmin:I
Landroid/app/admin/SecurityLog$SecurityEvent;-><init>([B)V
+Landroid/app/admin/SecurityLog;->writeEvent(I[Ljava/lang/Object;)I
Landroid/app/AlarmManager;->FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED:I
Landroid/app/AlarmManager;->FLAG_IDLE_UNTIL:I
Landroid/app/AlarmManager;->FLAG_STANDALONE:I
@@ -196,12 +211,14 @@
Landroid/app/AppOpsManager;->OP_READ_CONTACTS:I
Landroid/app/AppOpsManager;->OP_READ_PHONE_STATE:I
Landroid/app/AppOpsManager;->OP_READ_SMS:I
+Landroid/app/AppOpsManager;->OP_RUN_IN_BACKGROUND:I
Landroid/app/AppOpsManager;->OP_VIBRATE:I
Landroid/app/AppOpsManager;->OP_WIFI_SCAN:I
Landroid/app/AppOpsManager;->OP_WRITE_CONTACTS:I
Landroid/app/AppOpsManager;->OP_WRITE_SMS:I
Landroid/app/AppOpsManager;->permissionToOpCode(Ljava/lang/String;)I
Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I
+Landroid/app/backup/AbsoluteFileBackupHelper;-><init>(Landroid/content/Context;[Ljava/lang/String;)V
Landroid/app/backup/BackupDataInput$EntityHeader;->dataSize:I
Landroid/app/backup/BackupDataInput$EntityHeader;->key:Ljava/lang/String;
Landroid/app/backup/BackupDataInputStream;->dataSize:I
@@ -209,6 +226,7 @@
Landroid/app/backup/BackupDataOutput;->mBackupWriter:J
Landroid/app/backup/BackupHelperDispatcher$Header;->chunkSize:I
Landroid/app/backup/BackupHelperDispatcher$Header;->keyPrefix:Ljava/lang/String;
+Landroid/app/backup/BlobBackupHelper;-><init>(I[Ljava/lang/String;)V
Landroid/app/backup/FileBackupHelperBase;->writeNewStateDescription(Landroid/os/ParcelFileDescriptor;)V
Landroid/app/backup/FullBackup;->backupToTar(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I
Landroid/app/backup/FullBackupDataOutput;->addSize(J)V
@@ -245,7 +263,10 @@
Landroid/app/Dialog;->mListenersHandler:Landroid/os/Handler;
Landroid/app/Dialog;->mOwnerActivity:Landroid/app/Activity;
Landroid/app/Dialog;->mShowMessage:Landroid/os/Message;
+Landroid/app/DownloadManager;->forceDownload([J)V
+Landroid/app/DownloadManager;->markRowDeleted([J)I
Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri;
+Landroid/app/DownloadManager;->restartDownload([J)V
Landroid/app/FragmentManagerImpl;->mAdded:Ljava/util/ArrayList;
Landroid/app/FragmentManagerImpl;->noteStateNotSaved()V
Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl;
@@ -425,14 +446,24 @@
Landroid/bluetooth/BluetoothHeadset;->startScoUsingVirtualVoiceCall(Landroid/bluetooth/BluetoothDevice;)Z
Landroid/bluetooth/BluetoothHeadset;->stopScoUsingVirtualVoiceCall(Landroid/bluetooth/BluetoothDevice;)Z
Landroid/bluetooth/BluetoothMapClient;->sendMessage(Landroid/bluetooth/BluetoothDevice;[Landroid/net/Uri;Ljava/lang/String;Landroid/app/PendingIntent;Landroid/app/PendingIntent;)Z
+Landroid/bluetooth/BluetoothPan;->close()V
+Landroid/bluetooth/BluetoothPan;->connect(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothPan;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothPan;->doBind()Z
+Landroid/bluetooth/BluetoothPan;-><init>(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;)V
+Landroid/bluetooth/BluetoothPan;->isEnabled()Z
Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z
+Landroid/bluetooth/BluetoothPan;->isValidDevice(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothPan;->log(Ljava/lang/String;)V
Landroid/bluetooth/BluetoothPan;->setBluetoothTethering(Z)V
+Landroid/bluetooth/BluetoothProfile;->PAN:I
Landroid/bluetooth/BluetoothUuid;->RESERVED_UUIDS:[Landroid/os/ParcelUuid;
Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String;
Landroid/bluetooth/IBluetoothManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth;
Landroid/bluetooth/IBluetooth$Stub$Proxy;->getAddress()Ljava/lang/String;
Landroid/bluetooth/le/ScanRecord;->parseFromBytes([B)Landroid/bluetooth/le/ScanRecord;
+Landroid/content/AsyncTaskLoader$LoadTask;->doInBackground([Ljava/lang/Void;)Ljava/lang/Object;
Landroid/content/AsyncTaskLoader;->mExecutor:Ljava/util/concurrent/Executor;
Landroid/content/BroadcastReceiver$PendingResult;-><init>(ILjava/lang/String;Landroid/os/Bundle;IZZLandroid/os/IBinder;II)V
Landroid/content/BroadcastReceiver;->setPendingResult(Landroid/content/BroadcastReceiver$PendingResult;)V
@@ -499,6 +530,7 @@
Landroid/content/pm/ApplicationInfo;->scanSourceDir:Ljava/lang/String;
Landroid/content/pm/ApplicationInfo;->secondaryCpuAbi:Ljava/lang/String;
Landroid/content/pm/ApplicationInfo;->secondaryNativeLibraryDir:Ljava/lang/String;
+Landroid/content/pm/ApplicationInfo;->versionCode:I
Landroid/content/pm/ComponentInfo;->getComponentName()Landroid/content/ComponentName;
Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
Landroid/content/pm/IPackageManager;->addPermissionAsync(Landroid/content/pm/PermissionInfo;)Z
@@ -621,6 +653,7 @@
Landroid/content/res/ColorStateList;->mDefaultColor:I
Landroid/content/res/ColorStateList;->mFactory:Landroid/content/res/ColorStateList$ColorStateListFactory;
Landroid/content/res/ColorStateList;->mStateSpecs:[[I
+Landroid/content/res/ColorStateList;->onColorsChanged()V
Landroid/content/res/CompatibilityInfo;->applicationScale:F
Landroid/content/res/CompatibilityInfo;->DEFAULT_COMPATIBILITY_INFO:Landroid/content/res/CompatibilityInfo;
Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
@@ -678,6 +711,7 @@
Landroid/database/AbstractCursor;->mNotifyUri:Landroid/net/Uri;
Landroid/database/AbstractCursor;->mRowIdColumnIndex:I
Landroid/database/AbstractWindowedCursor;->clearOrCreateWindow(Ljava/lang/String;)V
+Landroid/database/CursorJoiner;->compareStrings([Ljava/lang/String;)I
Landroid/database/CursorWindow;->mWindowPtr:J
Landroid/database/CursorWindow;->sCursorWindowSize:I
Landroid/database/CursorWindow;->sWindowToPidMap:Landroid/util/LongSparseArray;
@@ -689,12 +723,21 @@
Landroid/database/sqlite/SQLiteDatabase;->CONFLICT_VALUES:[Ljava/lang/String;
Landroid/database/sqlite/SQLiteDatabase;->mConfigurationLocked:Landroid/database/sqlite/SQLiteDatabaseConfiguration;
Landroid/database/sqlite/SQLiteDatabase;->mConnectionPoolLocked:Landroid/database/sqlite/SQLiteConnectionPool;
+Landroid/database/sqlite/SQLiteDatabase;->reopenReadWrite()V
Landroid/database/sqlite/SQLiteDebug$PagerStats;->largestMemAlloc:I
Landroid/database/sqlite/SQLiteDebug$PagerStats;->memoryUsed:I
Landroid/database/sqlite/SQLiteDebug$PagerStats;->pageCacheOverflow:I
Landroid/database/sqlite/SQLiteOpenHelper;->mName:Ljava/lang/String;
Landroid/database/sqlite/SQLiteStatement;-><init>(Landroid/database/sqlite/SQLiteDatabase;Ljava/lang/String;[Ljava/lang/Object;)V
Landroid/ddm/DdmHandleAppName;->getAppName()Ljava/lang/String;
+Landroid/filterfw/core/AsyncRunner$AsyncRunnerTask;->doInBackground([Landroid/filterfw/core/SyncRunner;)Landroid/filterfw/core/AsyncRunner$RunnerResult;
+Landroid/filterfw/core/FilterFunction;->executeWithArgList([Ljava/lang/Object;)Landroid/filterfw/core/Frame;
+Landroid/filterfw/core/Filter;->initWithAssignmentList([Ljava/lang/Object;)V
+Landroid/filterfw/core/KeyValueMap;->fromKeyValues([Ljava/lang/Object;)Landroid/filterfw/core/KeyValueMap;
+Landroid/filterfw/core/KeyValueMap;->setKeyValues([Ljava/lang/Object;)V
+Landroid/filterfw/FilterFunctionEnvironment;->createFunction(Ljava/lang/Class;[Ljava/lang/Object;)Landroid/filterfw/core/FilterFunction;
+Landroid/filterfw/GraphEnvironment;->addReferences([Ljava/lang/Object;)V
+Landroid/filterfw/io/GraphReader;->addReferencesByKeysAndValues([Ljava/lang/Object;)V
Landroid/graphics/AvoidXfermode$Mode;->AVOID:Landroid/graphics/AvoidXfermode$Mode;
Landroid/graphics/AvoidXfermode$Mode;->TARGET:Landroid/graphics/AvoidXfermode$Mode;
Landroid/graphics/BaseCanvas;->mNativeCanvasWrapper:J
@@ -736,6 +779,7 @@
Landroid/graphics/drawable/Drawable;->getOpticalInsets()Landroid/graphics/Insets;
Landroid/graphics/drawable/Drawable;->inflateWithAttributes(Landroid/content/res/Resources;Lorg/xmlpull/v1/XmlPullParser;Landroid/content/res/TypedArray;I)V
Landroid/graphics/drawable/Drawable;->mCallback:Ljava/lang/ref/WeakReference;
+Landroid/graphics/drawable/Drawable;->parseTintMode(ILandroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuff$Mode;
Landroid/graphics/drawable/GradientDrawable$GradientState;->mAngle:I
Landroid/graphics/drawable/GradientDrawable$GradientState;->mGradientColors:[I
Landroid/graphics/drawable/GradientDrawable$GradientState;->mGradient:I
@@ -748,12 +792,14 @@
Landroid/graphics/drawable/GradientDrawable$GradientState;->mRadiusArray:[F
Landroid/graphics/drawable/GradientDrawable$GradientState;->mRadius:F
Landroid/graphics/drawable/GradientDrawable$GradientState;->mShape:I
+Landroid/graphics/drawable/GradientDrawable$GradientState;->mSolidColors:Landroid/content/res/ColorStateList;
Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeDashGap:F
Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeDashWidth:F
Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeWidth:I
Landroid/graphics/drawable/GradientDrawable$GradientState;->mThickness:I
Landroid/graphics/drawable/GradientDrawable$GradientState;->mThicknessRatio:F
Landroid/graphics/drawable/GradientDrawable$GradientState;->mWidth:I
+Landroid/graphics/drawable/GradientDrawable;->mGradientState:Landroid/graphics/drawable/GradientDrawable$GradientState;
Landroid/graphics/drawable/GradientDrawable;->mPadding:Landroid/graphics/Rect;
Landroid/graphics/drawable/Icon;->getBitmap()Landroid/graphics/Bitmap;
Landroid/graphics/drawable/Icon;->getDataBytes()[B
@@ -763,9 +809,13 @@
Landroid/graphics/drawable/Icon;->hasTint()Z
Landroid/graphics/drawable/Icon;->mType:I
Landroid/graphics/drawable/InsetDrawable;->mState:Landroid/graphics/drawable/InsetDrawable$InsetState;
+Landroid/graphics/drawable/LayerDrawable$ChildDrawable;->mDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/graphics/drawable/LayerDrawable$LayerState;->mChildren:[Landroid/graphics/drawable/LayerDrawable$ChildDrawable;
Landroid/graphics/drawable/LayerDrawable;->mLayerState:Landroid/graphics/drawable/LayerDrawable$LayerState;
Landroid/graphics/drawable/NinePatchDrawable;->mNinePatchState:Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;
Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;->mNinePatch:Landroid/graphics/NinePatch;
+Landroid/graphics/drawable/RippleDrawable;->mState:Landroid/graphics/drawable/RippleDrawable$RippleState;
+Landroid/graphics/drawable/RippleDrawable$RippleState;->mColor:Landroid/content/res/ColorStateList;
Landroid/graphics/drawable/StateListDrawable;->extractStateSet(Landroid/util/AttributeSet;)[I
Landroid/graphics/drawable/StateListDrawable;->getStateCount()I
Landroid/graphics/drawable/StateListDrawable;->getStateDrawable(I)Landroid/graphics/drawable/Drawable;
@@ -785,6 +835,8 @@
Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V
Landroid/graphics/GraphicBuffer;->mNativeObject:J
Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I
+Landroid/graphics/Insets;->left:I
+Landroid/graphics/Insets;->right:I
Landroid/graphics/LinearGradient;->mColors:[I
Landroid/graphics/Matrix;->native_instance:J
Landroid/graphics/Movie;-><init>(J)V
@@ -867,7 +919,11 @@
Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_BLUE:Landroid/hardware/camera2/CaptureResult$Key;
Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_GREEN:Landroid/hardware/camera2/CaptureResult$Key;
Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_RED:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/impl/CameraMetadataNative;->areValuesAllNull([Ljava/lang/Object;)Z
Landroid/hardware/camera2/impl/CameraMetadataNative;->mMetadataPtr:J
+Landroid/hardware/camera2/utils/HashCodeHelpers;->hashCode([F)I
+Landroid/hardware/camera2/utils/HashCodeHelpers;->hashCodeGeneric([Ljava/lang/Object;)I
+Landroid/hardware/camera2/utils/HashCodeHelpers;->hashCode([I)I
Landroid/hardware/Camera;->addCallbackBuffer([BI)V
Landroid/hardware/Camera;->mNativeContext:J
Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera;
@@ -934,15 +990,25 @@
Landroid/hardware/usb/UsbRequest;->mLength:I
Landroid/hardware/usb/UsbRequest;->mNativeContext:J
Landroid/icu/impl/CurrencyData;-><init>()V
+Landroid/icu/impl/locale/XLocaleDistance$RegionMapper$Builder;->addParadigms([Ljava/lang/String;)Landroid/icu/impl/locale/XLocaleDistance$RegionMapper$Builder;
+Landroid/icu/impl/TimeZoneGenericNames;->formatPattern(Landroid/icu/impl/TimeZoneGenericNames$Pattern;[Ljava/lang/String;)Ljava/lang/String;
+Landroid/icu/impl/TimeZoneGenericNames$GenericNameType;-><init>([Ljava/lang/String;)V
Landroid/icu/text/ArabicShaping;->isAlefMaksouraChar(C)Z
Landroid/icu/text/ArabicShaping;->isSeenTailFamilyChar(C)I
Landroid/icu/text/ArabicShaping;->isTailChar(C)Z
Landroid/icu/text/ArabicShaping;->isYehHamzaChar(C)Z
+Landroid/icu/text/Collator;->getIntValue(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)I
Landroid/icu/text/DateFormatSymbols;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
Landroid/icu/text/DateIntervalFormat;-><init>()V
Landroid/icu/text/DateTimePatternGenerator$DistanceInfo;-><init>()V
Landroid/icu/text/DecimalFormatSymbols;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
+Landroid/icu/text/DictionaryBreakEngine;-><init>([Ljava/lang/Integer;)V
+Landroid/icu/text/LocaleDisplayNames$LastResortLocaleDisplayNames;-><init>(Landroid/icu/util/ULocale;[Landroid/icu/text/DisplayContext;)V
+Landroid/icu/text/MeasureFormat;->formatMeasuresSlowTrack(Landroid/icu/text/ListFormatter;Ljava/lang/StringBuilder;Ljava/text/FieldPosition;[Landroid/icu/util/Measure;)Ljava/lang/StringBuilder;
Landroid/icu/text/RuleBasedCollator;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
+Landroid/icu/text/SimpleFormatter;->formatAndAppend(Ljava/lang/StringBuilder;[I[Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;
+Landroid/icu/text/SimpleFormatter;->formatAndReplace(Ljava/lang/StringBuilder;[I[Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;
+Landroid/icu/text/SimpleFormatter;->format([Ljava/lang/CharSequence;)Ljava/lang/String;
Landroid/icu/text/SpoofChecker$ScriptSet;->and(I)V
Landroid/icu/text/SpoofChecker$ScriptSet;-><init>()V
Landroid/icu/text/SpoofChecker$ScriptSet;->isFull()Z
@@ -952,6 +1018,11 @@
Landroid/icu/text/Transliterator;->transliterate(Ljava/lang/String;)Ljava/lang/String;
Landroid/icu/text/UFormat;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
Landroid/icu/util/Calendar;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
+Landroid/icu/util/Currency$EquivalenceRelation;->add([Ljava/lang/Object;)Landroid/icu/util/Currency$EquivalenceRelation;
+Landroid/icu/util/GenderInfo;->getListGender([Landroid/icu/util/GenderInfo$Gender;)Landroid/icu/util/GenderInfo$Gender;
+Landroid/icu/util/LocaleMatcher;->getBestMatch([Landroid/icu/util/ULocale;)Landroid/icu/util/ULocale;
+Landroid/icu/util/LocalePriorityList;->add([Landroid/icu/util/ULocale;)Landroid/icu/util/LocalePriorityList$Builder;
+Landroid/icu/util/LocalePriorityList$Builder;->add([Landroid/icu/util/ULocale;)Landroid/icu/util/LocalePriorityList$Builder;
Landroid/inputmethodservice/InputMethodService;->mExtractEditText:Landroid/inputmethodservice/ExtractEditText;
Landroid/inputmethodservice/InputMethodService;->mRootView:Landroid/view/View;
Landroid/inputmethodservice/InputMethodService;->mSettingsObserver:Landroid/inputmethodservice/InputMethodService$SettingsObserver;
@@ -978,6 +1049,7 @@
Landroid/media/AudioFormat;->mEncoding:I
Landroid/media/AudioFormat;->mSampleRate:I
Landroid/media/audiofx/AudioEffect;->command(I[B[B)I
+Landroid/media/audiofx/AudioEffect;->concatArrays([[B)[B
Landroid/media/audiofx/AudioEffect;->getParameter([I[B)I
Landroid/media/audiofx/AudioEffect;->getParameter([I[I)I
Landroid/media/audiofx/AudioEffect;-><init>(Ljava/util/UUID;Ljava/util/UUID;II)V
@@ -1053,6 +1125,8 @@
Landroid/media/AudioTrack;->mStreamType:I
Landroid/media/AudioTrack;->native_release()V
Landroid/media/AudioTrack;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V
+Landroid/media/effect/SingleFilterEffect;-><init>(Landroid/media/effect/EffectContext;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/media/effect/SizeChangeEffect;-><init>(Landroid/media/effect/EffectContext;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
Landroid/media/ExifInterface;->getDateTime()J
Landroid/media/IAudioService;->getStreamMaxVolume(I)I
Landroid/media/IAudioService;->getStreamVolume(I)I
@@ -1137,6 +1211,7 @@
Landroid/media/SubtitleTrack$RenderingWidget;->setSize(II)V
Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
Landroid/media/ToneGenerator;->mNativeContext:J
+Landroid/media/tv/TvInputService$OverlayViewCleanUpTask;->doInBackground([Landroid/view/View;)Ljava/lang/Void;
Landroid/media/VolumeShaper$Configuration;-><init>(IIIDI[F[F)V
Landroid/media/VolumeShaper$Configuration;->mDurationMs:D
Landroid/media/VolumeShaper$Configuration;->mId:I
@@ -1190,6 +1265,7 @@
Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V
Landroid/net/LocalSocketImpl;->inboundFileDescriptors:[Ljava/io/FileDescriptor;
Landroid/net/LocalSocketImpl;->outboundFileDescriptors:[Ljava/io/FileDescriptor;
+Landroid/net/MacAddress;->addr([I)[B
Landroid/net/NetworkCapabilities;->getCapabilities()[I
Landroid/net/NetworkCapabilities;->getTransportTypes()[I
Landroid/net/NetworkPolicyManager;->mService:Landroid/net/INetworkPolicyManager;
@@ -1234,6 +1310,7 @@
Landroid/net/SSLCertificateSocketFactory;->setChannelIdPrivateKey(Ljava/security/PrivateKey;)V
Landroid/net/SSLCertificateSocketFactory;->setSoWriteTimeout(Ljava/net/Socket;I)V
Landroid/net/SSLCertificateSocketFactory;->TAG:Ljava/lang/String;
+Landroid/net/SSLCertificateSocketFactory;->toLengthPrefixedList([[B)[B
Landroid/net/SSLCertificateSocketFactory;->verifyHostname(Ljava/net/Socket;Ljava/lang/String;)V
Landroid/net/SSLSessionCache;->mSessionCache:Lcom/android/org/conscrypt/SSLClientSessionCache;
Landroid/net/StaticIpConfiguration;->gateway:Ljava/net/InetAddress;
@@ -1305,10 +1382,12 @@
Landroid/net/wifi/WifiSsid;->NONE:Ljava/lang/String;
Landroid/nfc/NfcAdapter;->getDefaultAdapter()Landroid/nfc/NfcAdapter;
Landroid/nfc/NfcAdapter;->setNdefPushMessageCallback(Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;Landroid/app/Activity;I)V
+Landroid/nfc/TechListParcel;-><init>([[Ljava/lang/String;)V
Landroid/opengl/GLSurfaceView$EglHelper;->mEglContext:Ljavax/microedition/khronos/egl/EGLContext;
Landroid/opengl/GLSurfaceView$GLThread;->mEglHelper:Landroid/opengl/GLSurfaceView$EglHelper;
Landroid/opengl/GLSurfaceView;->mGLThread:Landroid/opengl/GLSurfaceView$GLThread;
Landroid/opengl/GLSurfaceView;->mRenderer:Landroid/opengl/GLSurfaceView$Renderer;
+Landroid/os/AsyncTask$AsyncTaskResult;-><init>(Landroid/os/AsyncTask;[Ljava/lang/Object;)V
Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask;
Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status;
Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean;
@@ -1316,6 +1395,7 @@
Landroid/os/AsyncTask;->sDefaultExecutor:Ljava/util/concurrent/Executor;
Landroid/os/AsyncTask;->setDefaultExecutor(Ljava/util/concurrent/Executor;)V
Landroid/os/BatteryStats$Counter;->getCountLocked(I)I
+Landroid/os/BatteryStats;->dumpLine(Ljava/io/PrintWriter;ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
Landroid/os/BatteryStats;->getUidStats()Landroid/util/SparseArray;
Landroid/os/BatteryStats$HistoryItem;->CMD_UPDATE:B
Landroid/os/BatteryStats$HistoryItem;->states2:I
@@ -1345,6 +1425,7 @@
Landroid/os/BatteryStats$Uid$Proc;->getUserTime(I)J
Landroid/os/BatteryStats$Uid$Sensor;->getHandle()I
Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer;
+Landroid/os/BestClock;-><init>(Ljava/time/ZoneId;[Ljava/time/Clock;)V
Landroid/os/Binder;->execTransact(IJJI)Z
Landroid/os/Binder;->mObject:J
Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
@@ -1385,6 +1466,7 @@
Landroid/os/Debug$MemoryInfo;->otherSwappedOut:I
Landroid/os/Debug$MemoryInfo;->otherSwappedOutPss:I
Landroid/os/Environment;->buildExternalStorageAppDataDirs(Ljava/lang/String;)[Ljava/io/File;
+Landroid/os/Environment;->buildPaths([Ljava/io/File;[Ljava/lang/String;)[Ljava/io/File;
Landroid/os/Environment;->getVendorDirectory()Ljava/io/File;
Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File;
Landroid/os/FileObserver$ObserverThread;->onEvent(IILjava/lang/String;)V
@@ -1458,6 +1540,7 @@
Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z
Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V
Landroid/os/Process;->setArgV0(Ljava/lang/String;)V
+Landroid/os/RecoverySystem;->bootCommand(Landroid/content/Context;[Ljava/lang/String;)V
Landroid/os/SELinux;->isSELinuxEnabled()Z
Landroid/os/SELinux;->isSELinuxEnforced()Z
Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;)V
@@ -1477,6 +1560,7 @@
Landroid/os/storage/StorageManager;->getBestVolumeDescription(Landroid/os/storage/VolumeInfo;)Ljava/lang/String;
Landroid/os/storage/StorageManager;->getDisks()Ljava/util/List;
Landroid/os/storage/StorageManager;->getStorageBytesUntilLow(Ljava/io/File;)J
+Landroid/os/storage/StorageManager;->getStorageFullBytes(Ljava/io/File;)J
Landroid/os/storage/StorageManager;->getStorageLowBytes(Ljava/io/File;)J
Landroid/os/storage/StorageManager;->getVolumeList(II)[Landroid/os/storage/StorageVolume;
Landroid/os/storage/StorageManager;->getVolumeList()[Landroid/os/storage/StorageVolume;
@@ -1496,8 +1580,10 @@
Landroid/os/storage/VolumeInfo;->isPrimary()Z
Landroid/os/storage/VolumeInfo;->isVisible()Z
Landroid/os/StrictMode;->disableDeathOnFileUriExposure()V
+Landroid/os/StrictMode;->enterCriticalSpan(Ljava/lang/String;)Landroid/os/StrictMode$Span;
Landroid/os/StrictMode;->getThreadPolicyMask()I
Landroid/os/StrictMode;->onBinderStrictModePolicyChange(I)V
+Landroid/os/StrictMode$Span;->finish()V
Landroid/os/StrictMode$ThreadPolicy$Builder;->penaltyListener(Landroid/os/StrictMode$OnThreadViolationListener;Ljava/util/concurrent/Executor;)Landroid/os/StrictMode$ThreadPolicy$Builder;
Landroid/os/StrictMode;->violationsBeingTimed:Ljava/lang/ThreadLocal;
Landroid/os/SystemProperties;->addChangeCallback(Ljava/lang/Runnable;)V
@@ -1505,6 +1591,7 @@
Landroid/os/SystemProperties;->native_get(Ljava/lang/String;)Ljava/lang/String;
Landroid/os/SystemProperties;->PROP_NAME_MAX:I
Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/SystemService;->waitForAnyStopped([Ljava/lang/String;)V
Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V
Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V
Landroid/os/Trace;->isTagEnabled(J)Z
@@ -1616,6 +1703,7 @@
Landroid/preference/Preference;->performClick(Landroid/preference/PreferenceScreen;)V
Landroid/preference/PreferenceScreen;->mRootAdapter:Landroid/widget/ListAdapter;
Landroid/print/PrinterId;->getServiceName()Landroid/content/ComponentName;
+Landroid/print/PrintFileDocumentAdapter$WriteFileAsyncTask;->doInBackground([Ljava/lang/Void;)Ljava/lang/Void;
Landroid/print/PrintJobInfo;->getAdvancedOptions()Landroid/os/Bundle;
Landroid/print/PrintJobInfo;->getDocumentInfo()Landroid/print/PrintDocumentInfo;
Landroid/provider/Browser$BookmarkColumns;->DATE:Ljava/lang/String;
@@ -1631,6 +1719,7 @@
Landroid/provider/Browser;->updateVisitedHistory(Landroid/content/ContentResolver;Ljava/lang/String;Z)V
Landroid/provider/CalendarContract$CalendarAlerts;->findNextAlarmTime(Landroid/content/ContentResolver;J)J
Landroid/provider/CalendarContract$CalendarAlerts;->rescheduleMissedAlarms(Landroid/content/ContentResolver;Landroid/content/Context;Landroid/app/AlarmManager;)V
+Landroid/provider/Downloads$Impl;->CONTENT_URI:Landroid/net/Uri;
Landroid/provider/Settings$ContentProviderHolder;->mContentProvider:Landroid/content/IContentProvider;
Landroid/provider/Settings$Global;->ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED:Ljava/lang/String;
Landroid/provider/Settings$Global;->PACKAGE_VERIFIER_ENABLE:Ljava/lang/String;
@@ -1856,6 +1945,8 @@
Landroid/R$styleable;->Window:[I
Landroid/R$styleable;->Window_windowBackground:I
Landroid/R$styleable;->Window_windowFrame:I
+Landroid/security/Credentials;->convertToPem([Ljava/security/cert/Certificate;)[B
+Landroid/security/keymaster/KeymasterArguments;->addEnums(I[I)V
Landroid/security/keystore/AndroidKeyStoreProvider;->getKeyStoreOperationHandle(Ljava/lang/Object;)J
Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore;
Landroid/security/net/config/RootTrustManager;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
@@ -1868,10 +1959,13 @@
Landroid/service/media/MediaBrowserService$Result;->mFlags:I
Landroid/service/notification/NotificationListenerService;->registerAsSystemService(Landroid/content/Context;Landroid/content/ComponentName;I)V
Landroid/service/notification/NotificationListenerService;->unregisterAsSystemService()V
+Landroid/service/notification/StatusBarNotification;->getUid()I
Landroid/service/voice/AlwaysOnHotwordDetector$EventPayload;->getCaptureSession()Ljava/lang/Integer;
+Landroid/service/voice/AlwaysOnHotwordDetector$RefreshAvailabiltyTask;->doInBackground([Ljava/lang/Void;)Ljava/lang/Void;
Landroid/service/voice/VoiceInteractionService;->isKeyphraseAndLocaleSupportedForHotword(Ljava/lang/String;Ljava/util/Locale;)Z
Landroid/service/vr/IVrManager;->getVr2dDisplayId()I
Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V
+Landroid/speech/tts/TextToSpeech$Connection$SetupConnectionAsyncTask;->doInBackground([Ljava/lang/Void;)Ljava/lang/Integer;
Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String;
Landroid/system/Int32Ref;->value:I
Landroid/system/OsConstants;->AF_NETLINK:I
@@ -1930,6 +2024,24 @@
Landroid/system/OsConstants;->XATTR_REPLACE:I
Landroid/system/StructTimeval;->fromMillis(J)Landroid/system/StructTimeval;
Landroid/telecom/AudioState;->isMuted:Z
+Landroid/telecom/Log;->addEvent(Landroid/telecom/Logging/EventManager$Loggable;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->buildMessage(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+Landroid/telecom/Log;->d(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->d(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->e(Ljava/lang/Object;Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->e(Ljava/lang/String;Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Logging/EventManager;->event(Landroid/telecom/Logging/EventManager$Loggable;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->i(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->i(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->v(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->v(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->w(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->w(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->wtf(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->wtf(Ljava/lang/Object;Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->wtf(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->wtf(Ljava/lang/String;Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Response;->onResult(Ljava/lang/Object;[Ljava/lang/Object;)V
Landroid/telecom/TelecomManager;->EXTRA_IS_HANDOVER:Ljava/lang/String;
Landroid/telecom/TelecomManager;->getUserSelectedOutgoingPhoneAccount()Landroid/telecom/PhoneAccountHandle;
Landroid/telecom/TelecomManager;->setUserSelectedOutgoingPhoneAccount(Landroid/telecom/PhoneAccountHandle;)V
@@ -1978,6 +2090,7 @@
Landroid/telephony/SubscriptionManager;->getPhoneId(I)I
Landroid/telephony/SubscriptionManager;->getSlotIndex(I)I
Landroid/telephony/SubscriptionManager;->getSubId(I)[I
+Landroid/telephony/SubscriptionManager;->setDefaultDataSubId(I)V
Landroid/telephony/SubscriptionManager;->setDefaultSmsSubId(I)V
Landroid/telephony/TelephonyManager;->from(Landroid/content/Context;)Landroid/telephony/TelephonyManager;
Landroid/telephony/TelephonyManager;->getCallState(I)I
@@ -2014,6 +2127,7 @@
Landroid/telephony/TelephonyManager;->isWifiCallingAvailable()Z
Landroid/telephony/TelephonyManager;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
Landroid/text/AndroidBidi;->bidi(I[C[B)I
+Landroid/text/BoringLayout;->isBoring(Ljava/lang/CharSequence;Landroid/text/TextPaint;Landroid/text/TextDirectionHeuristic;Landroid/text/BoringLayout$Metrics;)Landroid/text/BoringLayout$Metrics;
Landroid/text/DynamicLayout;-><init>(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZIIILandroid/text/TextUtils$TruncateAt;I)V
Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout;
Landroid/text/Html;->withinStyle(Ljava/lang/StringBuilder;Ljava/lang/CharSequence;II)V
@@ -2079,9 +2193,11 @@
Landroid/text/TextLine;->obtain()Landroid/text/TextLine;
Landroid/text/TextLine;->sCached:[Landroid/text/TextLine;
Landroid/text/TextPaint;->setUnderlineText(IF)V
+Landroid/text/TextUtils$TruncateAt;->END_SMALL:Landroid/text/TextUtils$TruncateAt;
Landroid/transition/ChangeBounds;->BOTTOM_RIGHT_ONLY_PROPERTY:Landroid/util/Property;
Landroid/transition/ChangeBounds;->POSITION_PROPERTY:Landroid/util/Property;
Landroid/transition/TransitionManager;->sRunningTransitions:Ljava/lang/ThreadLocal;
+Landroid/transition/TransitionUtils;->mergeTransitions([Landroid/transition/Transition;)Landroid/transition/Transition;
Landroid/util/ArrayMap;->append(Ljava/lang/Object;Ljava/lang/Object;)V
Landroid/util/ArrayMap;->mBaseCacheSize:I
Landroid/util/ArrayMap;->mTwiceBaseCacheSize:I
@@ -2092,6 +2208,7 @@
Landroid/util/LongSparseLongArray;->mKeys:[J
Landroid/util/LongSparseLongArray;->mSize:I
Landroid/util/LongSparseLongArray;->mValues:[J
+Landroid/util/MathUtils;->constrain(III)I
Landroid/util/NtpTrustedTime;->forceRefresh()Z
Landroid/util/NtpTrustedTime;->getCachedNtpTime()J
Landroid/util/NtpTrustedTime;->getCachedNtpTimeReference()J
@@ -2100,6 +2217,7 @@
Landroid/util/Pools$SimplePool;->mPool:[Ljava/lang/Object;
Landroid/util/Pools$SynchronizedPool;->acquire()Ljava/lang/Object;
Landroid/util/Pools$SynchronizedPool;-><init>(I)V
+Landroid/util/Pools$SynchronizedPool;->release(Ljava/lang/Object;)Z
Landroid/util/Rational;->mDenominator:I
Landroid/util/Rational;->mNumerator:I
Landroid/util/Singleton;->get()Ljava/lang/Object;
@@ -2127,6 +2245,7 @@
Landroid/view/accessibility/IAccessibilityManager;->getEnabledAccessibilityServiceList(II)Ljava/util/List;
Landroid/view/accessibility/IAccessibilityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/accessibility/IAccessibilityManager;
Landroid/view/accessibility/IAccessibilityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/view/animation/Animation;->detach()V
Landroid/view/animation/Animation;->initializeInvalidateRegion(IIII)V
Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener;
Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -2171,10 +2290,12 @@
Landroid/view/inputmethod/InputMethodManager;->focusIn(Landroid/view/View;)V
Landroid/view/inputmethod/InputMethodManager;->focusOut(Landroid/view/View;)V
Landroid/view/inputmethod/InputMethodManager;->getInputMethodWindowVisibleHeight()I
+Landroid/view/inputmethod/InputMethodManager;->getInstance()Landroid/view/inputmethod/InputMethodManager;
Landroid/view/inputmethod/InputMethodManager;->mCurId:Ljava/lang/String;
Landroid/view/inputmethod/InputMethodManager;->mCurRootView:Landroid/view/View;
Landroid/view/inputmethod/InputMethodManager;->mH:Landroid/view/inputmethod/InputMethodManager$H;
Landroid/view/inputmethod/InputMethodManager;->mNextServedView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->mServedInputConnectionWrapper:Landroid/view/inputmethod/InputMethodManager$ControlledInputConnectionWrapper;
Landroid/view/inputmethod/InputMethodManager;->mServedView:Landroid/view/View;
Landroid/view/inputmethod/InputMethodManager;->mService:Lcom/android/internal/view/IInputMethodManager;
Landroid/view/inputmethod/InputMethodManager;->notifyUserAction()V
@@ -2196,10 +2317,11 @@
Landroid/view/IWindowManager$Stub$Proxy;->getInitialDisplayDensity(I)I
Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar()Z
Landroid/view/IWindowManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
+Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
Landroid/view/KeyCharacterMap$FallbackAction;->keyCode:I
Landroid/view/KeyCharacterMap$FallbackAction;->metaState:I
Landroid/view/KeyCharacterMap;-><init>(J)V
+Landroid/view/KeyEvent;->isConfirmKey(I)Z
Landroid/view/KeyEvent;->mAction:I
Landroid/view/KeyEvent;->mCharacters:Ljava/lang/String;
Landroid/view/KeyEvent;->mDeviceId:I
@@ -2252,6 +2374,9 @@
Landroid/view/RemoteAnimationTarget;->taskId:I
Landroid/view/RemoteAnimationTarget;->windowConfiguration:Landroid/app/WindowConfiguration;
Landroid/view/RenderNodeAnimator;->callOnFinished(Landroid/view/RenderNodeAnimator;)V
+Landroid/view/RenderNodeAnimator;-><init>(IF)V
+Landroid/view/RenderNodeAnimator;->mapViewPropertyToRenderProperty(I)I
+Landroid/view/RenderNodeAnimator;->setTarget(Landroid/view/View;)V
Landroid/view/RenderNode;->discardDisplayList()V
Landroid/view/RenderNode;->output()V
Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener;
@@ -2288,6 +2413,9 @@
Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextSelection;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionStarted(I)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
Landroid/view/textclassifier/TextClassificationManager;->getTextClassifier(I)Landroid/view/textclassifier/TextClassifier;
+Landroid/view/textclassifier/TextClassifier;->classifyText(Ljava/lang/CharSequence;IILandroid/view/textclassifier/TextClassification$Options;)Landroid/view/textclassifier/TextClassification;
+Landroid/view/textclassifier/TextClassifier;->generateLinks(Ljava/lang/CharSequence;Landroid/view/textclassifier/TextLinks$Options;)Landroid/view/textclassifier/TextLinks;
+Landroid/view/textclassifier/TextClassifier;->suggestSelection(Ljava/lang/CharSequence;IILandroid/view/textclassifier/TextSelection$Options;)Landroid/view/textclassifier/TextSelection;
Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z
Landroid/view/TextureView;->destroyHardwareLayer()V
Landroid/view/TextureView;->destroyHardwareResources()V
@@ -2368,6 +2496,7 @@
Landroid/view/View$ListenerInfo;->mOnTouchListener:Landroid/view/View$OnTouchListener;
Landroid/view/View;->mAccessibilityDelegate:Landroid/view/View$AccessibilityDelegate;
Landroid/view/View;->mAttachInfo:Landroid/view/View$AttachInfo;
+Landroid/view/View;->mBackground:Landroid/graphics/drawable/Drawable;
Landroid/view/View;->mBottom:I
Landroid/view/View;->mContext:Landroid/content/Context;
Landroid/view/View;->mDrawingCache:Landroid/graphics/Bitmap;
@@ -2409,7 +2538,10 @@
Landroid/view/View;->resetResolvedTextDirection()V
Landroid/view/View;->resetRtlProperties()V
Landroid/view/ViewRootImpl;->detachFunctor(J)V
+Landroid/view/ViewRootImpl;->dispatchInputEvent(Landroid/view/InputEvent;Landroid/view/InputEventReceiver;)V
+Landroid/view/ViewRootImpl;->dispatchInputEvent(Landroid/view/InputEvent;)V
Landroid/view/ViewRootImpl;->enqueueInputEvent(Landroid/view/InputEvent;)V
+Landroid/view/ViewRootImpl;->getWindowFlags()I
Landroid/view/ViewRootImpl;->invokeFunctor(JZ)V
Landroid/view/ViewRootImpl;->mStopped:Z
Landroid/view/ViewRootImpl;->mSurface:Landroid/view/Surface;
@@ -2545,6 +2677,7 @@
Landroid/widget/AbsSeekBar;->mThumb:Landroid/graphics/drawable/Drawable;
Landroid/widget/AbsSeekBar;->mTouchProgressOffset:F
Landroid/widget/ActivityChooserModel;->get(Landroid/content/Context;Ljava/lang/String;)Landroid/widget/ActivityChooserModel;
+Landroid/widget/ActivityChooserModel$PersistHistoryAsyncTask;->doInBackground([Ljava/lang/Object;)Ljava/lang/Void;
Landroid/widget/ActivityChooserView;->setExpandActivityOverflowButtonDrawable(Landroid/graphics/drawable/Drawable;)V
Landroid/widget/AdapterView;->mDataChanged:Z
Landroid/widget/AdapterView;->mFirstPosition:I
@@ -2573,6 +2706,7 @@
Landroid/widget/Editor;->mShowSoftInputOnFocus:Z
Landroid/widget/ExpandableListView;->mChildDivider:Landroid/graphics/drawable/Drawable;
Landroid/widget/ExpandableListView;->mGroupIndicator:Landroid/graphics/drawable/Drawable;
+Landroid/widget/FastScroller;->groupAnimatorOfFloat(Landroid/util/Property;F[Landroid/view/View;)Landroid/animation/Animator;
Landroid/widget/FastScroller;->mContainerRect:Landroid/graphics/Rect;
Landroid/widget/FastScroller;->mHeaderCount:I
Landroid/widget/FastScroller;->mLongList:Z
@@ -2628,6 +2762,7 @@
Landroid/widget/NumberPicker;->mInputText:Landroid/widget/EditText;
Landroid/widget/NumberPicker;->mSelectionDivider:Landroid/graphics/drawable/Drawable;
Landroid/widget/NumberPicker;->mSelectorWheelPaint:Landroid/graphics/Paint;
+Landroid/widget/OverScroller;->isScrollingInDirection(FF)Z
Landroid/widget/OverScroller;->mScrollerY:Landroid/widget/OverScroller$SplineOverScroller;
Landroid/widget/OverScroller$SplineOverScroller;->mCurrVelocity:F
Landroid/widget/PopupMenu;->mPopup:Lcom/android/internal/view/menu/MenuPopupHelper;
@@ -2655,12 +2790,14 @@
Landroid/widget/PopupWindow;->setLayoutInScreenEnabled(Z)V
Landroid/widget/PopupWindow;->setLayoutInsetDecor(Z)V
Landroid/widget/PopupWindow;->setTouchModal(Z)V
+Landroid/widget/PopupWindow;->showAtLocation(Landroid/os/IBinder;III)V
Landroid/widget/ProgressBar;->mCurrentDrawable:Landroid/graphics/drawable/Drawable;
Landroid/widget/ProgressBar;->mDuration:I
Landroid/widget/ProgressBar;->mIndeterminate:Z
Landroid/widget/ProgressBar;->mMaxHeight:I
Landroid/widget/ProgressBar;->mMinHeight:I
Landroid/widget/ProgressBar;->mOnlyIndeterminate:Z
+Landroid/widget/RelativeLayout$DependencyGraph;->getSortedViews([Landroid/view/View;[I)V
Landroid/widget/RelativeLayout$LayoutParams;->mBottom:I
Landroid/widget/RelativeLayout$LayoutParams;->mLeft:I
Landroid/widget/RelativeLayout$LayoutParams;->mRight:I
@@ -2670,6 +2807,7 @@
Landroid/widget/RemoteViews$Action;->viewId:I
Landroid/widget/RemoteViewsAdapter;->mCache:Landroid/widget/RemoteViewsAdapter$FixedSizeRemoteViewsCache;
Landroid/widget/RemoteViewsAdapter;->mWorkerThread:Landroid/os/HandlerThread;
+Landroid/widget/RemoteViews$AsyncApplyTask;->doInBackground([Ljava/lang/Void;)Landroid/widget/RemoteViews$ViewTree;
Landroid/widget/RemoteViews$BitmapCache;->mBitmaps:Ljava/util/ArrayList;
Landroid/widget/RemoteViews$BitmapReflectionAction;->bitmap:Landroid/graphics/Bitmap;
Landroid/widget/RemoteViews$BitmapReflectionAction;->methodName:Ljava/lang/String;
@@ -2700,6 +2838,7 @@
Landroid/widget/SearchView;->mSearchSrcTextView:Landroid/widget/SearchView$SearchAutoComplete;
Landroid/widget/SearchView;->onCloseClicked()V
Landroid/widget/SearchView;->setQuery(Ljava/lang/CharSequence;)V
+Landroid/widget/SelectionActionModeHelper$TextClassificationAsyncTask;->doInBackground([Ljava/lang/Void;)Landroid/widget/SelectionActionModeHelper$SelectionResult;
Landroid/widget/SlidingDrawer;->mTopOffset:I
Landroid/widget/Spinner;->mPopup:Landroid/widget/Spinner$SpinnerPopup;
Landroid/widget/Switch;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
@@ -3004,6 +3143,7 @@
Lcom/android/internal/R$styleable;->DialogPreference:[I
Lcom/android/internal/R$styleable;->EdgeEffect_colorEdgeEffect:I
Lcom/android/internal/R$styleable;->EdgeEffect:[I
+Lcom/android/internal/R$styleable;->GridView:[I
Lcom/android/internal/R$styleable;->IconMenuView:[I
Lcom/android/internal/R$styleable;->ImageView:[I
Lcom/android/internal/R$styleable;->ImageView_src:I
@@ -3047,6 +3187,7 @@
Lcom/android/internal/R$styleable;->SyncAdapter_userVisible:I
Lcom/android/internal/R$styleable;->TabWidget:[I
Lcom/android/internal/R$styleable;->TextAppearance:[I
+Lcom/android/internal/R$styleable;->TextViewAppearance:[I
Lcom/android/internal/R$styleable;->TextView_drawableBottom:I
Lcom/android/internal/R$styleable;->TextView_drawableLeft:I
Lcom/android/internal/R$styleable;->TextView_drawableRight:I
@@ -3093,6 +3234,11 @@
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_endCall:I
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I
+Lcom/android/internal/telephony/SmsHeader;->concatRef:Lcom/android/internal/telephony/SmsHeader$ConcatRef;
+Lcom/android/internal/telephony/SmsHeader$ConcatRef;->msgCount:I
+Lcom/android/internal/telephony/SmsHeader$ConcatRef;->refNumber:I
+Lcom/android/internal/telephony/SmsHeader$ConcatRef;->seqNumber:I
+Lcom/android/internal/telephony/SmsMessageBase;->mUserDataHeader:Lcom/android/internal/telephony/SmsHeader;
Lcom/android/internal/telephony/SmsRawData;->CREATOR:Landroid/os/Parcelable$Creator;
Lcom/android/internal/telephony/SmsRawData;-><init>([B)V
Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -3101,6 +3247,8 @@
Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
Lcom/android/internal/util/XmlUtils;->skipCurrentTag(Lorg/xmlpull/v1/XmlPullParser;)V
Lcom/android/internal/util/XmlUtils;->writeMapXml(Ljava/util/Map;Ljava/io/OutputStream;)V
+Lcom/android/internal/view/IInputConnectionWrapper;->mInputConnection:Landroid/view/inputmethod/InputConnection;
+Lcom/android/internal/view/IInputConnectionWrapper;->mLock:Ljava/lang/Object;
Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager;
Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;->getEnabledInputMethodList()Ljava/util/List;
Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -3111,11 +3259,19 @@
Lcom/android/internal/view/menu/MenuBuilder;->setOptionalIconsVisible(Z)V
Lcom/android/internal/view/menu/MenuItemImpl;->mIconResId:I
Lcom/android/internal/view/menu/MenuItemImpl;->setMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
+Lcom/android/internal/view/menu/MenuPopupHelper;->mForceShowIcon:Z
Lcom/android/internal/view/menu/MenuPopupHelper;->setForceShowIcon(Z)V
Lcom/android/internal/view/menu/MenuView$ItemView;->getItemData()Lcom/android/internal/view/menu/MenuItemImpl;
+Lcom/android/okhttp/CertificatePinner$Builder;->add(Ljava/lang/String;[Ljava/lang/String;)Lcom/android/okhttp/CertificatePinner$Builder;
+Lcom/android/okhttp/CertificatePinner;->check(Ljava/lang/String;[Ljava/security/cert/Certificate;)V
Lcom/android/okhttp/ConnectionPool;->keepAliveDurationNs:J
Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
+Lcom/android/okhttp/ConnectionSpec$Builder;->cipherSuites([Lcom/android/okhttp/CipherSuite;)Lcom/android/okhttp/ConnectionSpec$Builder;
+Lcom/android/okhttp/ConnectionSpec$Builder;->cipherSuites([Ljava/lang/String;)Lcom/android/okhttp/ConnectionSpec$Builder;
+Lcom/android/okhttp/ConnectionSpec$Builder;->tlsVersions([Lcom/android/okhttp/TlsVersion;)Lcom/android/okhttp/ConnectionSpec$Builder;
+Lcom/android/okhttp/ConnectionSpec$Builder;->tlsVersions([Ljava/lang/String;)Lcom/android/okhttp/ConnectionSpec$Builder;
+Lcom/android/okhttp/Headers;->of([Ljava/lang/String;)Lcom/android/okhttp/Headers;
Lcom/android/okhttp/HttpUrl;->encodedPath()Ljava/lang/String;
Lcom/android/okhttp/HttpUrl;->query()Ljava/lang/String;
Lcom/android/okhttp/internal/http/HttpEngine;->httpStream:Lcom/android/okhttp/internal/http/HttpStream;
@@ -3123,11 +3279,13 @@
Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest(Lcom/android/okhttp/Request;)Lcom/android/okhttp/Request;
Lcom/android/okhttp/internal/http/HttpEngine;->priorResponse:Lcom/android/okhttp/Response;
Lcom/android/okhttp/internal/http/HttpEngine;->userResponse:Lcom/android/okhttp/Response;
+Lcom/android/okhttp/internal/NamedRunnable;-><init>(Ljava/lang/String;[Ljava/lang/Object;)V
Lcom/android/okhttp/OkHttpClient;->connectionPool:Lcom/android/okhttp/ConnectionPool;
Lcom/android/okhttp/OkHttpClient;->DEFAULT_PROTOCOLS:Ljava/util/List;
Lcom/android/okhttp/OkHttpClient;->dns:Lcom/android/okhttp/Dns;
Lcom/android/okhttp/OkHttpClient;->setProtocols(Ljava/util/List;)Lcom/android/okhttp/OkHttpClient;
Lcom/android/okhttp/OkHttpClient;->setRetryOnConnectionFailure(Z)V
+Lcom/android/okhttp/okio/ByteString;->of([B)Lcom/android/okhttp/okio/ByteString;
Lcom/android/okhttp/Request;->headers:Lcom/android/okhttp/Headers;
Lcom/android/okhttp/Request;->method:Ljava/lang/String;
Lcom/android/okhttp/Request;->url:Lcom/android/okhttp/HttpUrl;
@@ -3248,6 +3406,7 @@
Ljava/lang/AbstractStringBuilder;->value:[C
Ljava/lang/Boolean;->value:Z
Ljava/lang/Byte;->value:B
+Ljava/lang/Character$UnicodeBlock;-><init>(Ljava/lang/String;[Ljava/lang/String;)V
Ljava/lang/Character;->value:C
Ljava/lang/Class;->accessFlags:I
Ljava/lang/Class;->dexCache:Ljava/lang/Object;
@@ -3276,9 +3435,12 @@
Ljava/lang/ref/FinalizerReference;->next:Ljava/lang/ref/FinalizerReference;
Ljava/lang/ref/FinalizerReference;->queue:Ljava/lang/ref/ReferenceQueue;
Ljava/lang/ref/FinalizerReference;->remove(Ljava/lang/ref/FinalizerReference;)V
+Ljava/lang/reflect/Constructor;->newInstance0([Ljava/lang/Object;)Ljava/lang/Object;
Ljava/lang/reflect/Executable;->artMethod:J
Ljava/lang/reflect/Parameter;-><init>(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V
+Ljava/lang/reflect/Proxy;->getProxyClass0(Ljava/lang/ClassLoader;[Ljava/lang/Class;)Ljava/lang/Class;
Ljava/lang/reflect/Proxy;->invoke(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/ref/Reference;->getReferent()Ljava/lang/Object;
Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V
Ljava/lang/ref/Reference;->referent:Ljava/lang/Object;
Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
@@ -3286,7 +3448,9 @@
Ljava/lang/Runtime;->mLibPaths:[Ljava/lang/String;
Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
Ljava/lang/Short;->value:S
+Ljava/lang/String;->getCharsNoCheck(II[CI)V
Ljava/lang/String;-><init>(II[C)V
+Ljava/lang/System;->arraycopy([CI[CII)V
Ljava/lang/System;->arraycopy([II[III)V
Ljava/lang/System;-><init>()V
Ljava/lang/Thread;->daemon:Z
@@ -3355,6 +3519,9 @@
Ljava/nio/ByteBuffer;->offset:I
Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
Ljava/nio/DirectByteBuffer;-><init>(JI)V
+Ljava/nio/file/Files;->createAndCheckIsDirectory(Ljava/nio/file/Path;[Ljava/nio/file/attribute/FileAttribute;)V
+Ljava/nio/file/Files;->followLinks([Ljava/nio/file/LinkOption;)Z
+Ljava/nio/file/Files;->isAccessible(Ljava/nio/file/Path;[Ljava/nio/file/AccessMode;)Z
Ljava/nio/NIOAccess;->getBaseArray(Ljava/nio/Buffer;)Ljava/lang/Object;
Ljava/nio/NIOAccess;->getBaseArrayOffset(Ljava/nio/Buffer;)I
Ljava/nio/NIOAccess;->getBasePointer(Ljava/nio/Buffer;)J
@@ -3395,6 +3562,7 @@
Ljava/util/concurrent/ThreadPoolExecutor;->allowCoreThreadTimeOut:Z
Ljava/util/EnumMap;->keyType:Ljava/lang/Class;
Ljava/util/EnumSet;->elementType:Ljava/lang/Class;
+Ljava/util/Formatter$FormatSpecifier;->checkBadFlags([Ljava/util/Formatter$Flags;)V
Ljava/util/HashMap$HashIterator;->hasNext()Z
Ljava/util/HashMap;->modCount:I
Ljava/util/HashMap;->table:[Ljava/util/HashMap$Node;
@@ -3404,6 +3572,8 @@
Ljava/util/LinkedHashMap$LinkedHashIterator;->hasNext()Z
Ljava/util/LinkedList;->size:I
Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
+Ljava/util/logging/LogManager$Beans;->getConstructor(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
+Ljava/util/logging/LogManager$Beans;->getMethod(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
Ljava/util/PriorityQueue;->modCount:I
Ljava/util/PriorityQueue;->size:I
Ljava/util/Random;->seedUniquifier()J
@@ -3438,3 +3608,8 @@
Lorg/json/JSONObject;->writeTo(Lorg/json/JSONStringer;)V
Lorg/w3c/dom/traversal/NodeIterator;->nextNode()Lorg/w3c/dom/Node;
Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
+Lsun/security/x509/AlgorithmId;->oid([I)Lsun/security/util/ObjectIdentifier;
+Lsun/util/logging/PlatformLogger$DefaultLoggerProxy;->doLog(Lsun/util/logging/PlatformLogger$Level;Ljava/lang/String;[Ljava/lang/Object;)V
+Lsun/util/logging/PlatformLogger$DefaultLoggerProxy;->formatMessage(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+Lsun/util/logging/PlatformLogger$JavaLoggerProxy;->doLog(Lsun/util/logging/PlatformLogger$Level;Ljava/lang/String;[Ljava/lang/Object;)V
+Lsun/util/logging/PlatformLogger$LoggerProxy;->doLog(Lsun/util/logging/PlatformLogger$Level;Ljava/lang/String;[Ljava/lang/Object;)V
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index b598296..76bf510 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -501,7 +501,6 @@
Landroid/telephony/SubscriptionManager;->isValidSubscriptionId(I)Z
Landroid/telephony/SubscriptionManager;->putPhoneIdAndSubIdExtra(Landroid/content/Intent;II)V
Landroid/telephony/SubscriptionManager;->putPhoneIdAndSubIdExtra(Landroid/content/Intent;I)V
-Landroid/telephony/SubscriptionManager;->setDefaultDataSubId(I)V
Landroid/telephony/SubscriptionManager;->setDisplayName(Ljava/lang/String;IJ)I
Landroid/telephony/SubscriptionManager;->setIconTint(II)I
Landroid/telephony/TelephonyManager;->getIntAtIndex(Landroid/content/ContentResolver;Ljava/lang/String;I)I
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 27bbc4b..1084b42 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2165,30 +2165,18 @@
}
@Override
- public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
+ public Bundle getSuspendedPackageAppExtras() {
+ final PersistableBundle extras;
try {
- return mPM.getSuspendedPackageAppExtras(packageName, mContext.getUserId());
+ extras = mPM.getSuspendedPackageAppExtras(mContext.getOpPackageName(),
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- }
-
- @Override
- public Bundle getSuspendedPackageAppExtras() {
- final PersistableBundle extras = getSuspendedPackageAppExtras(mContext.getOpPackageName());
return extras != null ? new Bundle(extras.deepCopy()) : null;
}
@Override
- public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras) {
- try {
- mPM.setSuspendedPackageAppExtras(packageName, appExtras, mContext.getUserId());
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
-
- @Override
public boolean isPackageSuspendedForUser(String packageName, int userId) {
try {
return mPM.isPackageSuspendedForUser(packageName, userId);
@@ -2199,8 +2187,12 @@
/** @hide */
@Override
- public boolean isPackageSuspended(String packageName) {
- return isPackageSuspendedForUser(packageName, mContext.getUserId());
+ public boolean isPackageSuspended(String packageName) throws NameNotFoundException {
+ try {
+ return isPackageSuspendedForUser(packageName, mContext.getUserId());
+ } catch (IllegalArgumentException ie) {
+ throw new NameNotFoundException(packageName);
+ }
}
@Override
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4ab6724..2b4f420 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4588,10 +4588,18 @@
bindHeaderChronometerAndTime(contentView);
bindProfileBadge(contentView);
}
+ bindActivePermissions(contentView);
bindExpandButton(contentView);
mN.mUsesStandardHeader = true;
}
+ private void bindActivePermissions(RemoteViews contentView) {
+ int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
+ contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
+ contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
+ contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
+ }
+
private void bindExpandButton(RemoteViews contentView) {
int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
contentView.setDrawableTint(R.id.expand_button, false, color,
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 4a7cf62..9e47ced 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -328,7 +328,8 @@
* Group information is only used for presentation, not for behavior.
*
* Only modifiable before the channel is submitted to
- * {@link NotificationManager#notify(String, int, Notification)}.
+ * {@link NotificationManager#createNotificationChannel(NotificationChannel)}, unless the
+ * channel is not currently part of a group.
*
* @param groupId the id of a group created by
* {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.
@@ -341,6 +342,9 @@
* Sets whether notifications posted to this channel can appear as application icon badges
* in a Launcher.
*
+ * Only modifiable before the channel is submitted to
+ * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
+ *
* @param showBadge true if badges should be allowed to be shown.
*/
public void setShowBadge(boolean showBadge) {
@@ -353,7 +357,7 @@
* least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound.
*
* Only modifiable before the channel is submitted to
- * {@link NotificationManager#notify(String, int, Notification)}.
+ * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
*/
public void setSound(Uri sound, AudioAttributes audioAttributes) {
this.mSound = sound;
@@ -365,7 +369,7 @@
* on devices that support that feature.
*
* Only modifiable before the channel is submitted to
- * {@link NotificationManager#notify(String, int, Notification)}.
+ * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
*/
public void enableLights(boolean lights) {
this.mLights = lights;
@@ -376,7 +380,7 @@
* {@link #enableLights(boolean) enabled} on this channel and the device supports that feature.
*
* Only modifiable before the channel is submitted to
- * {@link NotificationManager#notify(String, int, Notification)}.
+ * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
*/
public void setLightColor(int argb) {
this.mLightColor = argb;
@@ -387,7 +391,7 @@
* be set with {@link #setVibrationPattern(long[])}.
*
* Only modifiable before the channel is submitted to
- * {@link NotificationManager#notify(String, int, Notification)}.
+ * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
*/
public void enableVibration(boolean vibration) {
this.mVibrationEnabled = vibration;
@@ -399,7 +403,7 @@
* vibration} as well. Otherwise, vibration will be disabled.
*
* Only modifiable before the channel is submitted to
- * {@link NotificationManager#notify(String, int, Notification)}.
+ * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
*/
public void setVibrationPattern(long[] vibrationPattern) {
this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
@@ -407,9 +411,10 @@
}
/**
- * Sets the level of interruption of this notification channel. Only
- * modifiable before the channel is submitted to
- * {@link NotificationManager#notify(String, int, Notification)}.
+ * Sets the level of interruption of this notification channel.
+ *
+ * Only modifiable before the channel is submitted to
+ * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
*
* @param importance the amount the user should be interrupted by
* notifications from this channel.
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 4a6fa8c..8783d94 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -23,6 +23,7 @@
import android.os.IStatsManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.AndroidException;
import android.util.Slog;
/**
@@ -82,55 +83,74 @@
}
/**
- * Clients can send a configuration and simultaneously registers the name of a broadcast
- * receiver that listens for when it should request data.
+ * Adds the given configuration and associates it with the given configKey. If a config with the
+ * given configKey already exists for the caller's uid, it is replaced with the new one.
*
* @param configKey An arbitrary integer that allows clients to track the configuration.
- * @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
+ * @param config Wire-encoded StatsdConfig proto that specifies metrics (and all
* dependencies eg, conditions and matchers).
- * @return true if successful
+ * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+ * @throws IllegalArgumentException if config is not a wire-encoded StatsdConfig proto
*/
@RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(long configKey, byte[] config) {
+ public void addConfig(long configKey, byte[] config) throws StatsUnavailableException {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.e(TAG, "Failed to find statsd when adding configuration");
- return false;
- }
- return service.addConfiguration(configKey, config);
+ service.addConfiguration(configKey, config); // can throw IllegalArgumentException
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when adding configuration");
- return false;
+ throw new StatsUnavailableException("could not connect", e);
}
}
}
/**
+ * TODO: Temporary for backwards compatibility. Remove.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean addConfiguration(long configKey, byte[] config) {
+ try {
+ addConfig(configKey, config);
+ return true;
+ } catch (StatsUnavailableException | IllegalArgumentException e) {
+ return false;
+ }
+ }
+
+ /**
* Remove a configuration from logging.
*
* @param configKey Configuration key to remove.
- * @return true if successful
+ * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
*/
@RequiresPermission(Manifest.permission.DUMP)
- public boolean removeConfiguration(long configKey) {
+ public void removeConfig(long configKey) throws StatsUnavailableException {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.e(TAG, "Failed to find statsd when removing configuration");
- return false;
- }
- return service.removeConfiguration(configKey);
+ service.removeConfiguration(configKey);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when removing configuration");
- return false;
+ throw new StatsUnavailableException("could not connect", e);
}
}
}
/**
+ * TODO: Temporary for backwards compatibility. Remove.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean removeConfiguration(long configKey) {
+ try {
+ removeConfig(configKey);
+ return true;
+ } catch (StatsUnavailableException e) {
+ return false;
+ }
+ }
+
+ /**
* Set the PendingIntent to be used when broadcasting subscriber information to the given
* subscriberId within the given config.
* <p>
@@ -150,123 +170,165 @@
* {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
* <p>
* This function can only be called by the owner (uid) of the config. It must be called each
- * time statsd starts. The config must have been added first (via addConfiguration()).
+ * time statsd starts. The config must have been added first (via {@link #addConfig}).
*
- * @param configKey The integer naming the config to which this subscriber is attached.
- * @param subscriberId ID of the subscriber, as used in the config.
* @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
* associated with the given subscriberId. May be null, in which case
* it undoes any previous setting of this subscriberId.
- * @return true if successful
+ * @param configKey The integer naming the config to which this subscriber is attached.
+ * @param subscriberId ID of the subscriber, as used in the config.
+ * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public void setBroadcastSubscriber(
+ PendingIntent pendingIntent, long configKey, long subscriberId)
+ throws StatsUnavailableException {
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (pendingIntent != null) {
+ // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
+ IBinder intentSender = pendingIntent.getTarget().asBinder();
+ service.setBroadcastSubscriber(configKey, subscriberId, intentSender);
+ } else {
+ service.unsetBroadcastSubscriber(configKey, subscriberId);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
+ throw new StatsUnavailableException("could not connect", e);
+ }
+ }
+ }
+
+ /**
+ * TODO: Temporary for backwards compatibility. Remove.
*/
@RequiresPermission(Manifest.permission.DUMP)
public boolean setBroadcastSubscriber(
long configKey, long subscriberId, PendingIntent pendingIntent) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.e(TAG, "Failed to find statsd when adding broadcast subscriber");
- return false;
- }
- if (pendingIntent != null) {
- // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
- IBinder intentSender = pendingIntent.getTarget().asBinder();
- return service.setBroadcastSubscriber(configKey, subscriberId, intentSender);
- } else {
- return service.unsetBroadcastSubscriber(configKey, subscriberId);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
- return false;
- }
+ try {
+ setBroadcastSubscriber(pendingIntent, configKey, subscriberId);
+ return true;
+ } catch (StatsUnavailableException e) {
+ return false;
}
}
/**
* Registers the operation that is called to retrieve the metrics data. This must be called
- * each time statsd starts. The config must have been added first (via addConfiguration(),
- * although addConfiguration could have been called on a previous boot). This operation allows
+ * each time statsd starts. The config must have been added first (via {@link #addConfig},
+ * although addConfig could have been called on a previous boot). This operation allows
* statsd to send metrics data whenever statsd determines that the metrics in memory are
- * approaching the memory limits. The fetch operation should call {@link #getData} to fetch the
- * data, which also deletes the retrieved metrics from statsd's memory.
+ * approaching the memory limits. The fetch operation should call {@link #getReports} to fetch
+ * the data, which also deletes the retrieved metrics from statsd's memory.
*
- * @param configKey The integer naming the config to which this operation is attached.
* @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
* associated with the given subscriberId. May be null, in which case
* it removes any associated pending intent with this configKey.
- * @return true if successful
+ * @param configKey The integer naming the config to which this operation is attached.
+ * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
*/
@RequiresPermission(Manifest.permission.DUMP)
- public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
+ public void setFetchReportsOperation(PendingIntent pendingIntent, long configKey)
+ throws StatsUnavailableException {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.e(TAG, "Failed to find statsd when registering data listener.");
- return false;
- }
if (pendingIntent == null) {
- return service.removeDataFetchOperation(configKey);
+ service.removeDataFetchOperation(configKey);
} else {
// Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
IBinder intentSender = pendingIntent.getTarget().asBinder();
- return service.setDataFetchOperation(configKey, intentSender);
+ service.setDataFetchOperation(configKey, intentSender);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when registering data listener.");
- return false;
+ throw new StatsUnavailableException("could not connect", e);
}
}
}
/**
- * Clients can request data with a binder call. This getter is destructive and also clears
- * the retrieved metrics from statsd memory.
- *
- * @param configKey Configuration key to retrieve data from.
- * @return Serialized ConfigMetricsReportList proto. Returns null on failure (eg, if statsd
- * crashed).
+ * TODO: Temporary for backwards compatibility. Remove.
*/
@RequiresPermission(Manifest.permission.DUMP)
- public @Nullable byte[] getData(long configKey) {
+ public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
+ try {
+ setFetchReportsOperation(pendingIntent, configKey);
+ return true;
+ } catch (StatsUnavailableException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Request the data collected for the given configKey.
+ * This getter is destructive - it also clears the retrieved metrics from statsd's memory.
+ *
+ * @param configKey Configuration key to retrieve data from.
+ * @return Serialized ConfigMetricsReportList proto.
+ * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public byte[] getReports(long configKey) throws StatsUnavailableException {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.e(TAG, "Failed to find statsd when getting data");
- return null;
- }
return service.getData(configKey);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when getting data");
- return null;
+ throw new StatsUnavailableException("could not connect", e);
+ }
+ }
+ }
+
+ /**
+ * TODO: Temporary for backwards compatibility. Remove.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public @Nullable byte[] getData(long configKey) {
+ try {
+ return getReports(configKey);
+ } catch (StatsUnavailableException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Clients can request metadata for statsd. Will contain stats across all configurations but not
+ * the actual metrics themselves (metrics must be collected via {@link #getReports(long)}.
+ * This getter is not destructive and will not reset any metrics/counters.
+ *
+ * @return Serialized StatsdStatsReport proto.
+ * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public byte[] getStatsMetadata() throws StatsUnavailableException {
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ return service.getMetadata();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to connect to statsd when getting metadata");
+ throw new StatsUnavailableException("could not connect", e);
}
}
}
/**
* Clients can request metadata for statsd. Will contain stats across all configurations but not
- * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
+ * the actual metrics themselves (metrics must be collected via {@link #getReports(long)}.
* This getter is not destructive and will not reset any metrics/counters.
*
* @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed).
*/
@RequiresPermission(Manifest.permission.DUMP)
public @Nullable byte[] getMetadata() {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.e(TAG, "Failed to find statsd when getting metadata");
- return null;
- }
- return service.getMetadata();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to connect to statsd when getting metadata");
- return null;
- }
+ try {
+ return getStatsMetadata();
+ } catch (StatsUnavailableException e) {
+ return null;
}
}
@@ -279,14 +341,33 @@
}
}
- private IStatsManager getIStatsManagerLocked() throws RemoteException {
+ private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException {
if (mService != null) {
return mService;
}
mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
- if (mService != null) {
+ if (mService == null) {
+ throw new StatsUnavailableException("could not be found");
+ }
+ try {
mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
+ } catch (RemoteException e) {
+ throw new StatsUnavailableException("could not connect when linkToDeath", e);
}
return mService;
}
+
+ /**
+ * Exception thrown when communication with the stats service fails (eg if it is not available).
+ * This might be thrown early during boot before the stats service has started or if it crashed.
+ */
+ public static class StatsUnavailableException extends AndroidException {
+ public StatsUnavailableException(String reason) {
+ super("Failed to connect to statsd: " + reason);
+ }
+
+ public StatsUnavailableException(String reason, Throwable e) {
+ super("Failed to connect to statsd: " + reason, e);
+ }
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 990147b..c491dcc 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1169,6 +1169,7 @@
* Constant to indicate the feature of mandatory backups. Used as argument to
* {@link #createAdminSupportIntent(String)}.
* @see #setMandatoryBackupTransport(ComponentName, ComponentName)
+ * @hide
*/
public static final String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
@@ -6843,8 +6844,7 @@
* @param restriction Indicates for which feature the dialog should be displayed. Can be a
* user restriction from {@link UserManager}, e.g.
* {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the constants
- * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
- * {@link #POLICY_MANDATORY_BACKUPS}.
+ * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
* @return Intent An intent to be used to start the dialog-activity if the restriction is
* set by an admin, or null if the restriction does not exist or no admin set it.
*/
@@ -8791,13 +8791,6 @@
*
* <p> Backup service is off by default when device owner is present.
*
- * <p> If backups are made mandatory by specifying a non-null mandatory backup transport using
- * the {@link DevicePolicyManager#setMandatoryBackupTransport} method, the backup service is
- * automatically enabled.
- *
- * <p> If the backup service is disabled using this method after the mandatory backup transport
- * has been set, the mandatory backup transport is cleared.
- *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled {@code true} to enable the backup service, {@code false} to disable it.
* @throws SecurityException if {@code admin} is not a device owner.
@@ -8835,6 +8828,8 @@
* <p>Only device owner can call this method.
* <p>If backups were disabled and a non-null backup transport {@link ComponentName} is
* specified, backups will be enabled.
+ * <p> If the backup service is disabled after the mandatory backup transport has been set, the
+ * mandatory backup transport is cleared.
*
* <p>NOTE: The method shouldn't be called on the main thread.
*
@@ -8842,6 +8837,7 @@
* @param backupTransportComponent The backup transport layer to be used for mandatory backups.
* @return {@code true} if the backup transport was successfully set; {@code false} otherwise.
* @throws SecurityException if {@code admin} is not a device owner.
+ * @hide
*/
@WorkerThread
public boolean setMandatoryBackupTransport(
@@ -8861,6 +8857,7 @@
*
* @return a {@link ComponentName} of the backup transport layer to be used if backups are
* mandatory or {@code null} if backups are not mandatory.
+ * @hide
*/
public ComponentName getMandatoryBackupTransport() {
throwIfParentInstance("getMandatoryBackupTransport");
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index a2aaf12..69852f3 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -25,11 +25,15 @@
void unpinSlice(String pkg, in Uri uri, in IBinder token);
boolean hasSliceAccess(String pkg);
SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
- int checkSlicePermission(in Uri uri, String pkg, int pid, int uid,
- in String[] autoGrantPermissions);
- void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
Uri[] getPinnedSlices(String pkg);
byte[] getBackupPayload(int user);
void applyRestore(in byte[] payload, int user);
+
+ // Perms.
+ void grantSlicePermission(String callingPkg, String toPkg, in Uri uri);
+ void revokeSlicePermission(String callingPkg, String toPkg, in Uri uri);
+ int checkSlicePermission(in Uri uri, String pkg, int pid, int uid,
+ in String[] autoGrantPermissions);
+ void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
}
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index bf3398a..4336f18 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -21,19 +21,13 @@
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.IContentProvider;
-import android.content.Intent;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -575,45 +569,4 @@
}
return sb.toString();
}
-
- /**
- * @deprecated TO BE REMOVED.
- */
- @Deprecated
- public static @Nullable Slice bindSlice(ContentResolver resolver,
- @NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
- Preconditions.checkNotNull(uri, "uri");
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
- Bundle extras = new Bundle();
- extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
- extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
- new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
- null, extras);
- Bundle.setDefusable(res, true);
- if (res == null) {
- return null;
- }
- return res.getParcelable(SliceProvider.EXTRA_SLICE);
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } finally {
- resolver.releaseProvider(provider);
- }
- }
-
- /**
- * @deprecated TO BE REMOVED.
- */
- @Deprecated
- public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
- @NonNull List<SliceSpec> supportedSpecs) {
- return context.getSystemService(SliceManager.class).bindSlice(intent, supportedSpecs);
- }
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 0285e9f..ad49437 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -16,6 +16,8 @@
package android.app.slice;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -38,6 +40,7 @@
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.UserHandle;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -47,6 +50,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* Class to handle interactions with {@link Slice}s.
@@ -101,22 +105,6 @@
private final IBinder mToken = new Binder();
/**
- * Permission denied.
- * @hide
- */
- public static final int PERMISSION_DENIED = -1;
- /**
- * Permission granted.
- * @hide
- */
- public static final int PERMISSION_GRANTED = 0;
- /**
- * Permission just granted by the user, and should be granted uri permission as well.
- * @hide
- */
- public static final int PERMISSION_USER_GRANTED = 1;
-
- /**
* @hide
*/
public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
@@ -140,7 +128,7 @@
* @see Intent#ACTION_ASSIST
* @see Intent#CATEGORY_HOME
*/
- public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
+ public void pinSlice(@NonNull Uri uri, @NonNull Set<SliceSpec> specs) {
try {
mService.pinSlice(mContext.getPackageName(), uri,
specs.toArray(new SliceSpec[specs.size()]), mToken);
@@ -150,6 +138,14 @@
}
/**
+ * @deprecated TO BE REMOVED
+ */
+ @Deprecated
+ public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
+ pinSlice(uri, new ArraySet<>(specs));
+ }
+
+ /**
* Remove a pin for a slice.
* <p>
* If the slice has no other pins/callbacks then the slice will be unpinned.
@@ -189,9 +185,10 @@
* into account all clients and returns only specs supported by all.
* @see SliceSpec
*/
- public @NonNull List<SliceSpec> getPinnedSpecs(Uri uri) {
+ public @NonNull Set<SliceSpec> getPinnedSpecs(Uri uri) {
try {
- return Arrays.asList(mService.getPinnedSpecs(uri, mContext.getPackageName()));
+ return new ArraySet<>(Arrays.asList(mService.getPinnedSpecs(uri,
+ mContext.getPackageName())));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -240,7 +237,7 @@
* @return The Slice provided by the app or null if none is given.
* @see Slice
*/
- public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+ public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull Set<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(uri, "uri");
ContentResolver resolver = mContext.getContentResolver();
try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
@@ -265,6 +262,14 @@
}
/**
+ * @deprecated TO BE REMOVED
+ */
+ @Deprecated
+ public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+ return bindSlice(uri, new ArraySet<>(supportedSpecs));
+ }
+
+ /**
* Turns a slice intent into a slice uri. Expects an explicit intent.
* <p>
* This goes through a several stage resolution process to determine if any slice
@@ -272,12 +277,12 @@
* <ol>
* <li> If the intent contains data that {@link ContentResolver#getType} is
* {@link SliceProvider#SLICE_TYPE} then the data will be returned.</li>
- * <li>If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then
- * the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result
- * will be returned.</li>
- * <li>Lastly, if the intent explicitly points at an activity, and that activity has
+ * <li>If the intent explicitly points at an activity, and that activity has
* meta-data for key {@link #SLICE_METADATA_KEY}, then the Uri specified there will be
* returned.</li>
+ * <li>Lastly, if the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then
+ * the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result
+ * will be returned.</li>
* <li>If no slice is found, then {@code null} is returned.</li>
* </ol>
* @param intent The intent associated with a slice.
@@ -287,37 +292,12 @@
* @see Intent
*/
public @Nullable Uri mapIntentToUri(@NonNull Intent intent) {
- Preconditions.checkNotNull(intent, "intent");
- Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
- || intent.getData() != null,
- "Slice intent must be explicit %s", intent);
ContentResolver resolver = mContext.getContentResolver();
-
- // Check if the intent has data for the slice uri on it and use that
- final Uri intentData = intent.getData();
- if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
- return intentData;
- }
+ final Uri staticUri = resolveStatic(intent, resolver);
+ if (staticUri != null) return staticUri;
// Otherwise ask the app
- Intent queryIntent = new Intent(intent);
- if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
- queryIntent.addCategory(CATEGORY_SLICE);
- }
- List<ResolveInfo> providers =
- mContext.getPackageManager().queryIntentContentProviders(queryIntent, 0);
- if (providers == null || providers.isEmpty()) {
- // There are no providers, see if this activity has a direct link.
- ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
- PackageManager.GET_META_DATA);
- if (resolve != null && resolve.activityInfo != null
- && resolve.activityInfo.metaData != null
- && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
- return Uri.parse(
- resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
- }
- return null;
- }
- String authority = providers.get(0).providerInfo.authority;
+ String authority = getAuthority(intent);
+ if (authority == null) return null;
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).build();
try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
@@ -338,10 +318,43 @@
}
}
+ private String getAuthority(Intent intent) {
+ Intent queryIntent = new Intent(intent);
+ if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
+ queryIntent.addCategory(CATEGORY_SLICE);
+ }
+ List<ResolveInfo> providers =
+ mContext.getPackageManager().queryIntentContentProviders(queryIntent, 0);
+ return providers != null && !providers.isEmpty() ? providers.get(0).providerInfo.authority
+ : null;
+ }
+
+ private Uri resolveStatic(@NonNull Intent intent, ContentResolver resolver) {
+ Preconditions.checkNotNull(intent, "intent");
+ Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
+ || intent.getData() != null,
+ "Slice intent must be explicit %s", intent);
+
+ // Check if the intent has data for the slice uri on it and use that
+ final Uri intentData = intent.getData();
+ if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+ return intentData;
+ }
+ // There are no providers, see if this activity has a direct link.
+ ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
+ PackageManager.GET_META_DATA);
+ if (resolve != null && resolve.activityInfo != null
+ && resolve.activityInfo.metaData != null
+ && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
+ return Uri.parse(
+ resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
+ }
+ return null;
+ }
+
/**
- * Turns a slice intent into slice content. Expects an explicit intent. If there is no
- * {@link android.content.ContentProvider} associated with the given intent this will throw
- * {@link IllegalArgumentException}.
+ * Turns a slice intent into slice content. Is a shortcut to perform the action
+ * of both {@link #mapIntentToUri(Intent)} and {@link #bindSlice(Uri, List)} at once.
*
* @param intent The intent associated with a slice.
* @param supportedSpecs List of supported specs.
@@ -351,34 +364,17 @@
* @see Intent
*/
public @Nullable Slice bindSlice(@NonNull Intent intent,
- @NonNull List<SliceSpec> supportedSpecs) {
+ @NonNull Set<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(intent, "intent");
Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
|| intent.getData() != null,
"Slice intent must be explicit %s", intent);
ContentResolver resolver = mContext.getContentResolver();
-
- // Check if the intent has data for the slice uri on it and use that
- final Uri intentData = intent.getData();
- if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
- return bindSlice(intentData, supportedSpecs);
- }
+ final Uri staticUri = resolveStatic(intent, resolver);
+ if (staticUri != null) return bindSlice(staticUri, supportedSpecs);
// Otherwise ask the app
- List<ResolveInfo> providers =
- mContext.getPackageManager().queryIntentContentProviders(intent, 0);
- if (providers == null || providers.isEmpty()) {
- // There are no providers, see if this activity has a direct link.
- ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
- PackageManager.GET_META_DATA);
- if (resolve != null && resolve.activityInfo != null
- && resolve.activityInfo.metaData != null
- && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
- return bindSlice(Uri.parse(resolve.activityInfo.metaData
- .getString(SLICE_METADATA_KEY)), supportedSpecs);
- }
- return null;
- }
- String authority = providers.get(0).providerInfo.authority;
+ String authority = getAuthority(intent);
+ if (authority == null) return null;
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).build();
try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
@@ -387,8 +383,6 @@
}
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
- extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
- new ArrayList<>(supportedSpecs));
final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
return null;
@@ -402,6 +396,16 @@
}
/**
+ * @deprecated TO BE REMOVED.
+ */
+ @Deprecated
+ @Nullable
+ public Slice bindSlice(@NonNull Intent intent,
+ @NonNull List<SliceSpec> supportedSpecs) {
+ return bindSlice(intent, new ArraySet<>(supportedSpecs));
+ }
+
+ /**
* Determine whether a particular process and user ID has been granted
* permission to access a specific slice URI.
*
@@ -417,9 +421,11 @@
* @see #grantSlicePermission(String, Uri)
*/
public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
- // TODO: Switch off Uri permissions.
- return mContext.checkUriPermission(uri, pid, uid,
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ try {
+ return mService.checkSlicePermission(uri, null, pid, uid, null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -431,11 +437,11 @@
* @see #revokeSlicePermission
*/
public void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
- // TODO: Switch off Uri permissions.
- mContext.grantUriPermission(toPackage, uri,
- Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+ try {
+ mService.grantSlicePermission(mContext.getPackageName(), toPackage, uri);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -453,11 +459,11 @@
* @see #grantSlicePermission
*/
public void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
- // TODO: Switch off Uri permissions.
- mContext.revokeUriPermission(toPackage, uri,
- Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+ try {
+ mService.revokeSlicePermission(mContext.getPackageName(), toPackage, uri);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -478,16 +484,6 @@
throw new SecurityException("User " + uid + " does not have slice permission for "
+ uri + ".");
}
- if (result == PERMISSION_USER_GRANTED) {
- // We just had a user grant of this permission and need to grant this to the app
- // permanently.
- mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(),
- Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
- // Notify a change has happened because we just granted a permission.
- mContext.getContentResolver().notifyChange(uri, null);
- }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index fe5742d..d369272 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -44,6 +44,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* A SliceProvider allows an app to provide content to be displayed in system spaces. This content
@@ -197,6 +198,14 @@
* @see {@link Slice}.
* @see {@link Slice#HINT_PARTIAL}
*/
+ public Slice onBindSlice(Uri sliceUri, Set<SliceSpec> supportedSpecs) {
+ return onBindSlice(sliceUri, new ArrayList<>(supportedSpecs));
+ }
+
+ /**
+ * @deprecated TO BE REMOVED
+ */
+ @Deprecated
public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
return null;
}
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 7252f02..216a4a0 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -237,20 +237,26 @@
DEFAULT_NETWORK_YES
})
@Retention(RetentionPolicy.SOURCE)
- public @interface DefaultNetwork {}
+ public @interface DefaultNetworkStatus {}
/**
- * Combined usage for this network regardless of whether it was the active default network.
+ * Combined usage for this network regardless of default network status.
*/
public static final int DEFAULT_NETWORK_ALL = -1;
/**
- * Usage that occurs while this network is not the active default network.
+ * Usage that occurs while this network is not a default network.
+ *
+ * <p>This implies that the app responsible for this usage requested that it occur on a
+ * specific network different from the one(s) the system would have selected for it.
*/
public static final int DEFAULT_NETWORK_NO = 0x1;
/**
- * Usage that occurs while this network is the active default network.
+ * Usage that occurs while this network is a default network.
+ *
+ * <p>This implies that the app either did not select a specific network for this usage,
+ * or it selected a network that the system could have selected for app traffic.
*/
public static final int DEFAULT_NETWORK_YES = 0x2;
@@ -262,7 +268,7 @@
private int mUid;
private int mTag;
private int mState;
- private int mDefaultNetwork;
+ private int mDefaultNetworkStatus;
private int mMetered;
private int mRoaming;
private long mBeginTimeStamp;
@@ -323,8 +329,9 @@
return 0;
}
- private static @DefaultNetwork int convertDefaultNetwork(int defaultNetwork) {
- switch (defaultNetwork) {
+ private static @DefaultNetworkStatus int convertDefaultNetworkStatus(
+ int defaultNetworkStatus) {
+ switch (defaultNetworkStatus) {
case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL;
case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO;
case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES;
@@ -397,18 +404,15 @@
}
/**
- * Default network state. One of the following values:<p/>
+ * Default network status. One of the following values:<p/>
* <ul>
* <li>{@link #DEFAULT_NETWORK_ALL}</li>
* <li>{@link #DEFAULT_NETWORK_NO}</li>
* <li>{@link #DEFAULT_NETWORK_YES}</li>
* </ul>
- * <p>Indicates whether the network usage occurred on the system default network for this
- * type of traffic, or whether the application chose to send this traffic on a network that
- * was not the one selected by the system.
*/
- public @DefaultNetwork int getDefaultNetwork() {
- return mDefaultNetwork;
+ public @DefaultNetworkStatus int getDefaultNetworkStatus() {
+ return mDefaultNetworkStatus;
}
/**
@@ -605,7 +609,7 @@
bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
- bucketOut.mDefaultNetwork = Bucket.convertDefaultNetwork(
+ bucketOut.mDefaultNetworkStatus = Bucket.convertDefaultNetworkStatus(
mRecycledSummaryEntry.defaultNetwork);
bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
@@ -657,7 +661,7 @@
bucketOut.mUid = Bucket.convertUid(getUid());
bucketOut.mTag = Bucket.convertTag(mTag);
bucketOut.mState = mState;
- bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL;
+ bucketOut.mDefaultNetworkStatus = Bucket.DEFAULT_NETWORK_ALL;
bucketOut.mMetered = Bucket.METERED_ALL;
bucketOut.mRoaming = Bucket.ROAMING_ALL;
bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 85f4efc..0b21196 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -35,6 +35,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.DataUnit;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -95,6 +96,15 @@
/** @hide */
public static final int CALLBACK_RELEASED = 1;
+ /**
+ * Minimum data usage threshold for registering usage callbacks.
+ *
+ * Requests registered with a threshold lower than this will only be triggered once this minimum
+ * is reached.
+ * @hide
+ */
+ public static final long MIN_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(2);
+
private final Context mContext;
private final INetworkStatsService mService;
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 84f57a3..503ca6c 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -111,7 +111,7 @@
/**
* An event type denoting a change in App Standby Bucket. The new bucket can be
- * retrieved by calling {@link #getStandbyBucket()}.
+ * retrieved by calling {@link #getAppStandbyBucket()}.
*
* @see UsageStatsManager#getAppStandbyBucket()
*/
@@ -326,13 +326,23 @@
* Returns the standby bucket of the app, if the event is of type
* {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
* @return the standby bucket associated with the event.
- *
+ * @hide
*/
public int getStandbyBucket() {
return (mBucketAndReason & 0xFFFF0000) >>> 16;
}
/**
+ * Returns the standby bucket of the app, if the event is of type
+ * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
+ * @return the standby bucket associated with the event.
+ *
+ */
+ public int getAppStandbyBucket() {
+ return (mBucketAndReason & 0xFFFF0000) >>> 16;
+ }
+
+ /**
* Returns the reason for the bucketing, if the event is of type
* {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
* the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 01ee671..f608fcb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -40,6 +40,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.ResultReceiver;
import android.os.ShellCommand;
@@ -1814,8 +1815,12 @@
public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
/**
- * Intent extra: A {@link Bundle} of extras for a package being suspended. Will be sent with
- * {@link #ACTION_MY_PACKAGE_SUSPENDED}.
+ * Intent extra: A {@link Bundle} of extras for a package being suspended. Will be sent as an
+ * extra with {@link #ACTION_MY_PACKAGE_SUSPENDED}.
+ *
+ * <p>The contents of this {@link Bundle} are a contract between the suspended app and the
+ * suspending app, i.e. any app with the permission {@code android.permission.SUSPEND_APPS}.
+ * This is meant to enable the suspended app to better handle the state of being suspended.
*
* @see #ACTION_MY_PACKAGE_SUSPENDED
* @see #ACTION_MY_PACKAGE_UNSUSPENDED
@@ -2284,6 +2289,10 @@
/**
* Activity Action: Started to show more details about why an application was suspended.
*
+ * <p>Whenever the system detects an activity launch for a suspended app, it shows a dialog to
+ * the user to inform them of the state and present them an affordance to start this activity
+ * action to show more details about the reason for suspension.
+ *
* <p>Apps holding {@link android.Manifest.permission#SUSPEND_APPS} must declare an activity
* handling this intent and protect it with
* {@link android.Manifest.permission#SEND_SHOW_SUSPENDED_APP_DETAILS}.
@@ -2293,6 +2302,8 @@
* <p class="note">This is a protected intent that can only be sent
* by the system.
*
+ * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, String)
* @see PackageManager#isPackageSuspended()
* @see #ACTION_PACKAGES_SUSPENDED
*
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 02ce47b8..2be33e9 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -280,9 +280,6 @@
PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId);
- void setSuspendedPackageAppExtras(String packageName, in PersistableBundle appExtras,
- int userId);
-
/**
* Backup/restore support - only the system uid may use these.
*/
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 9aace2e..8223363 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -212,7 +212,7 @@
* an applicaton.
*
* <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher,
- * any apps that override {@link #onPackagesSuspended(String[], Bundle, UserHandle)} will
+ * any apps that override {@link #onPackagesSuspended(String[], UserHandle, Bundle)} will
* not receive this callback.
*
* @param packageNames The names of the packages that have just been
@@ -226,15 +226,20 @@
* Indicates that one or more packages have been suspended. A device administrator or an app
* with {@code android.permission.SUSPEND_APPS} can do this.
*
- * @param packageNames The names of the packages that have just been suspended.
- * @param launcherExtras A {@link Bundle} of extras for the launcher.
- * @param user the user for which the given packages were suspended.
+ * <p>A suspending app with the permission {@code android.permission.SUSPEND_APPS} can
+ * optionally provide a {@link Bundle} of extra information that it deems helpful for the
+ * launcher to handle the suspended state of these packages. The contents of this
+ * {@link Bundle} supposed to be a contract between the suspending app and the launcher.
*
+ * @param packageNames The names of the packages that have just been suspended.
+ * @param user the user for which the given packages were suspended.
+ * @param launcherExtras A {@link Bundle} of extras for the launcher, if provided to the
+ * system, {@code null} otherwise.
* @see PackageManager#isPackageSuspended()
* @see #getSuspendedPackageLauncherExtras(String, UserHandle)
*/
- public void onPackagesSuspended(String[] packageNames, @Nullable Bundle launcherExtras,
- UserHandle user) {
+ public void onPackagesSuspended(String[] packageNames, UserHandle user,
+ @Nullable Bundle launcherExtras) {
onPackagesSuspended(packageNames, user);
}
@@ -662,6 +667,9 @@
* {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
* PersistableBundle, String)}.
*
+ * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending
+ * app and the launcher.
+ *
* <p>Note: This just returns whatever extras were provided to the system, <em>which might
* even be {@code null}.</em>
*
@@ -670,7 +678,7 @@
* @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently
* suspended.
*
- * @see Callback#onPackagesSuspended(String[], Bundle, UserHandle)
+ * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle)
* @see PackageManager#isPackageSuspended()
*/
public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
@@ -1298,8 +1306,8 @@
mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
break;
case MSG_SUSPENDED:
- mCallback.onPackagesSuspended(info.packageNames, info.launcherExtras,
- info.user);
+ mCallback.onPackagesSuspended(info.packageNames, info.user, info.launcherExtras
+ );
break;
case MSG_UNSUSPENDED:
mCallback.onPackagesUnsuspended(info.packageNames, info.user);
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 627ceb7..5f9f8f1 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -244,7 +244,7 @@
* the first position to be the same across updates.
*
* <strong>Deprecated</strong> This has been replaced by the
- * {@link PackageInfo#signingCertificateHistory} field, which takes into
+ * {@link PackageInfo#signingInfo} field, which takes into
* account signing certificate rotation. For backwards compatibility in
* the event of signing certificate rotation, this will return the oldest
* reported signing certificate, so that an application will appear to
@@ -256,29 +256,15 @@
public Signature[] signatures;
/**
- * Array of all signatures arrays read from the package file, potentially
+ * Signing information read from the package file, potentially
* including past signing certificates no longer used after signing
- * certificate rotation. Though signing certificate rotation is only
- * available for apps with a single signing certificate, this provides an
- * array of arrays so that packages signed with multiple signing
- * certificates can still return all signers. This is only filled in if
+ * certificate rotation. This is only filled in if
* the flag {@link PackageManager#GET_SIGNING_CERTIFICATES} was set.
*
- * A package must be singed with at least one certificate, which is at
- * position zero in the array. An application may be signed by multiple
- * certificates, which would be in the array at position zero in an
- * indeterminate order. A package may also have a history of certificates
- * due to signing certificate rotation. In this case, the array will be
- * populated by a series of single-entry arrays corresponding to a signing
- * certificate of the package.
- *
- * <strong>Note:</strong> Signature ordering is not guaranteed to be
- * stable which means that a package signed with certificates A and B is
- * equivalent to being signed with certificates B and A. This means that
- * in case multiple signatures are reported you cannot assume the one at
- * the first position will be the same across updates.
+ * Use this field instead of the deprecated {@code signatures} field.
+ * See {@link SigningInfo} for more information on its contents.
*/
- public Signature[][] signingCertificateHistory;
+ public SigningInfo signingInfo;
/**
* Application specified preferred configuration
@@ -476,17 +462,11 @@
dest.writeBoolean(mOverlayIsStatic);
dest.writeInt(compileSdkVersion);
dest.writeString(compileSdkVersionCodename);
- writeSigningCertificateHistoryToParcel(dest, parcelableFlags);
- }
-
- private void writeSigningCertificateHistoryToParcel(Parcel dest, int parcelableFlags) {
- if (signingCertificateHistory != null) {
- dest.writeInt(signingCertificateHistory.length);
- for (int i = 0; i < signingCertificateHistory.length; i++) {
- dest.writeTypedArray(signingCertificateHistory[i], parcelableFlags);
- }
+ if (signingInfo != null) {
+ dest.writeInt(1);
+ signingInfo.writeToParcel(dest, parcelableFlags);
} else {
- dest.writeInt(-1);
+ dest.writeInt(0);
}
}
@@ -544,7 +524,10 @@
mOverlayIsStatic = source.readBoolean();
compileSdkVersion = source.readInt();
compileSdkVersionCodename = source.readString();
- readSigningCertificateHistoryFromParcel(source);
+ int hasSigningInfo = source.readInt();
+ if (hasSigningInfo != 0) {
+ signingInfo = SigningInfo.CREATOR.createFromParcel(source);
+ }
// The component lists were flattened with the redundant ApplicationInfo
// instances omitted. Distribute the canonical one here as appropriate.
@@ -556,16 +539,6 @@
}
}
- private void readSigningCertificateHistoryFromParcel(Parcel source) {
- int len = source.readInt();
- if (len != -1) {
- signingCertificateHistory = new Signature[len][];
- for (int i = 0; i < len; i++) {
- signingCertificateHistory[i] = source.createTypedArray(Signature.CREATOR);
- }
- }
- }
-
private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
if (components != null) {
for (ComponentInfo ci : components) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 34e3d8b..9d3b53f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5571,7 +5571,8 @@
* @param packageName The name of the package to get the suspended status of.
* @param userId The user id.
* @return {@code true} if the package is suspended or {@code false} if the package is not
- * suspended or could not be found.
+ * suspended.
+ * @throws IllegalArgumentException if the package was not found.
* @hide
*/
public abstract boolean isPackageSuspendedForUser(String packageName, int userId);
@@ -5580,12 +5581,13 @@
* Query if an app is currently suspended.
*
* @return {@code true} if the given package is suspended, {@code false} otherwise
+ * @throws NameNotFoundException if the package could not be found.
*
* @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
* @hide
*/
@SystemApi
- public boolean isPackageSuspended(String packageName) {
+ public boolean isPackageSuspended(String packageName) throws NameNotFoundException {
throw new UnsupportedOperationException("isPackageSuspended not implemented");
}
@@ -5616,51 +5618,16 @@
}
/**
- * Retrieve the {@link PersistableBundle} that was passed as {@code appExtras} when the given
- * package was suspended.
+ * Returns a {@link Bundle} of extras that was meant to be sent to the calling app when it was
+ * suspended. An app with the permission {@code android.permission.SUSPEND_APPS} can supply this
+ * to the system at the time of suspending an app.
*
- * <p> The caller must hold permission {@link Manifest.permission#SUSPEND_APPS} to use this
- * api.</p>
+ * <p>This is the same {@link Bundle} that is sent along with the broadcast
+ * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED}, whenever the app is suspended. The contents of
+ * this {@link Bundle} are a contract between the suspended app and the suspending app.
*
- * @param packageName The package to retrieve extras for.
- * @return The {@code appExtras} for the suspended package.
- *
- * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.SUSPEND_APPS)
- public @Nullable PersistableBundle getSuspendedPackageAppExtras(String packageName) {
- throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
- }
-
- /**
- * Set the app extras for a suspended package. This method can be used to update the appExtras
- * for a package that was earlier suspended using
- * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
- * String)}
- * Does nothing if the given package is not already in a suspended state.
- *
- * @param packageName The package for which the appExtras need to be updated
- * @param appExtras The new appExtras for the given package
- *
- * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.SUSPEND_APPS)
- public void setSuspendedPackageAppExtras(String packageName,
- @Nullable PersistableBundle appExtras) {
- throw new UnsupportedOperationException("setSuspendedPackageAppExtras not implemented");
- }
-
- /**
- * Returns any extra information supplied as {@code appExtras} to the system when the calling
- * app was suspended.
- *
- * <p>Note: If no extras were supplied to the system, this method will return {@code null}, even
- * when the calling app has been suspended.</p>
+ * <p>Note: These extras are optional, so if no extras were supplied to the system, this method
+ * will return {@code null}, even when the calling app has been suspended.
*
* @return A {@link Bundle} containing the extras for the app, or {@code null} if the
* package is not currently suspended.
@@ -5668,6 +5635,7 @@
* @see #isPackageSuspended()
* @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
* @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+ * @see Intent#EXTRA_SUSPENDED_PACKAGE_EXTRAS
*/
public @Nullable Bundle getSuspendedPackageAppExtras() {
throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 699e81b..a9d0911 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -521,11 +521,6 @@
public abstract @Nullable PackageParser.Package getPackage(@NonNull String packageName);
/**
- * Returns a {@link com.android.server.pm.PackageSetting} for a given package name.
- */
- public abstract @Nullable Object getPackageSetting(String packageName);
-
- /**
* Returns a list without a change observer.
*
* {@see #getPackageList(PackageListObserver)}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3e0db60..7159f77 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -810,21 +810,11 @@
// replacement for GET_SIGNATURES
if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
- if (p.mSigningDetails.hasPastSigningCertificates()) {
- // Package has included signing certificate rotation information. Convert each
- // entry to an array
- int numberOfSigs = p.mSigningDetails.pastSigningCertificates.length;
- pi.signingCertificateHistory = new Signature[numberOfSigs][];
- for (int i = 0; i < numberOfSigs; i++) {
- pi.signingCertificateHistory[i] =
- new Signature[] { p.mSigningDetails.pastSigningCertificates[i] };
- }
- } else if (p.mSigningDetails.hasSignatures()) {
- // otherwise keep old behavior
- int numberOfSigs = p.mSigningDetails.signatures.length;
- pi.signingCertificateHistory = new Signature[1][numberOfSigs];
- System.arraycopy(p.mSigningDetails.signatures, 0,
- pi.signingCertificateHistory[0], 0, numberOfSigs);
+ if (p.mSigningDetails != SigningDetails.UNKNOWN) {
+ // only return a valid SigningInfo if there is signing information to report
+ pi.signingInfo = new SigningInfo(p.mSigningDetails);
+ } else {
+ pi.signingInfo = null;
}
}
return pi;
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
new file mode 100644
index 0000000..ef87403
--- /dev/null
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information pertaining to the signing certificates used to sign a package.
+ */
+public final class SigningInfo implements Parcelable {
+
+ @NonNull
+ private final PackageParser.SigningDetails mSigningDetails;
+
+ public SigningInfo() {
+ mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ }
+
+ /**
+ * @hide only packagemanager should be populating this
+ */
+ public SigningInfo(PackageParser.SigningDetails signingDetails) {
+ mSigningDetails = new PackageParser.SigningDetails(signingDetails);
+ }
+
+ public SigningInfo(SigningInfo orig) {
+ mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+ }
+
+ private SigningInfo(Parcel source) {
+ mSigningDetails = PackageParser.SigningDetails.CREATOR.createFromParcel(source);
+ }
+
+ /**
+ * Although relatively uncommon, packages may be signed by more than one signer, in which case
+ * their identity is viewed as being the set of all signers, not just any one.
+ */
+ public boolean hasMultipleSigners() {
+ return mSigningDetails.signatures != null && mSigningDetails.signatures.length > 1;
+ }
+
+ /**
+ * APK Signature Scheme v3 enables packages to provide a proof-of-rotation record that the
+ * platform verifies, and uses, to allow the use of new signing certificates. This is only
+ * available to packages that are not signed by multiple signers. In the event of a change to a
+ * new signing certificate, the package's past signing certificates are presented as well. Any
+ * check of a package's signing certificate should also include a search through its entire
+ * signing history, since it could change to a new signing certificate at any time.
+ */
+ public boolean hasPastSigningCertificates() {
+ return mSigningDetails.signatures != null
+ && mSigningDetails.pastSigningCertificates != null;
+ }
+
+ /**
+ * Returns the signing certificates this package has proven it is authorized to use. This
+ * includes both the signing certificate associated with the signer of the package and the past
+ * signing certificates it included as its proof of signing certificate rotation. This method
+ * is the preferred replacement for the {@code GET_SIGNATURES} flag used with {@link
+ * PackageManager#getPackageInfo(String, int)}. When determining if a package is signed by a
+ * desired certificate, the returned array should be checked to determine if it is one of the
+ * entries.
+ *
+ * <note>
+ * This method returns null if the package is signed by multiple signing certificates, as
+ * opposed to being signed by one current signer and also providing the history of past
+ * signing certificates. {@link #hasMultipleSigners()} may be used to determine if this
+ * package is signed by multiple signers. Packages which are signed by multiple signers
+ * cannot change their signing certificates and their {@code Signature} array should be
+ * checked to make sure that every entry matches the looked-for signing certificates.
+ * </note>
+ */
+ public Signature[] getSigningCertificateHistory() {
+ if (hasMultipleSigners()) {
+ return null;
+ } else if (!hasPastSigningCertificates()) {
+
+ // this package is only signed by one signer with no history, return it
+ return mSigningDetails.signatures;
+ } else {
+
+ // this package has provided proof of past signing certificates, include them
+ return mSigningDetails.pastSigningCertificates;
+ }
+ }
+
+ /**
+ * Returns the signing certificates used to sign the APK contents of this application. Not
+ * including any past signing certificates the package proved it is authorized to use.
+ * <note>
+ * This method should not be used unless {@link #hasMultipleSigners()} returns true,
+ * indicating that {@link #getSigningCertificateHistory()} cannot be used, otherwise {@link
+ * #getSigningCertificateHistory()} should be preferred.
+ * </note>
+ */
+ public Signature[] getApkContentsSigners() {
+ return mSigningDetails.signatures;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ mSigningDetails.writeToParcel(dest, parcelableFlags);
+ }
+
+ public static final Parcelable.Creator<SigningInfo> CREATOR =
+ new Parcelable.Creator<SigningInfo>() {
+ @Override
+ public SigningInfo createFromParcel(Parcel source) {
+ return new SigningInfo(source);
+ }
+
+ @Override
+ public SigningInfo[] newArray(int size) {
+ return new SigningInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 8732375..a61ea50 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -20,7 +20,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
@@ -666,7 +665,6 @@
* to create Network objects which are accessible to the Android system.
* @hide
*/
- @SystemApi
public static final class IpSecTunnelInterface implements AutoCloseable {
private final String mOpPackageName;
private final IIpSecService mService;
@@ -693,7 +691,6 @@
* @param prefixLen length of the InetAddress prefix
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
try {
@@ -713,7 +710,6 @@
* @param prefixLen length of the InetAddress prefix
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
try {
@@ -809,7 +805,6 @@
* @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
* @hide
*/
- @SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
@@ -836,7 +831,6 @@
* layer failure.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
@PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index fb5f46c..62f7996 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -22,7 +22,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
@@ -250,7 +249,6 @@
*
* @hide
*/
- @SystemApi
public static class NattKeepaliveCallback {
/** The specified {@code Network} is not connected. */
public static final int ERROR_INVALID_NETWORK = 1;
@@ -281,7 +279,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
@@ -324,7 +321,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
@@ -477,7 +473,6 @@
* @throws IOException indicating other errors
* @hide
*/
- @SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public IpSecTransform buildTunnelModeTransform(
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index c314a35..a8e8179 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -254,9 +254,8 @@
/**
* Indicates that this network is not congested.
* <p>
- * When a network is congested, the device should defer network traffic that
- * can be done at a later time without breaking developer contracts.
- * @hide
+ * When a network is congested, applications should defer network traffic
+ * that can be done at a later time, such as uploading analytics.
*/
public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 6f812ac..bd4a27c 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -168,11 +168,6 @@
* the requested network's required capabilities. Note that when searching
* for a network to satisfy a request, all capabilities requested must be
* satisfied.
- * <p>
- * If the given capability was previously added to the list of unwanted capabilities
- * then the capability will also be removed from the list of unwanted capabilities.
- *
- * @see #addUnwantedCapability(int)
*
* @param capability The capability to add.
* @return The builder to facilitate chaining
@@ -184,8 +179,7 @@
}
/**
- * Removes (if found) the given capability from this builder instance from both required
- * and unwanted capabilities lists.
+ * Removes (if found) the given capability from this builder instance.
*
* @param capability The capability to remove.
* @return The builder to facilitate chaining.
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 6363161..954071a 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -353,4 +353,20 @@
public static boolean isPlugWired(int plugType) {
return plugType == BATTERY_PLUGGED_USB || plugType == BATTERY_PLUGGED_AC;
}
+
+ /**
+ * Compute an approximation for how much time (in milliseconds) remains until the battery is
+ * fully charged. Returns -1 if no time can be computed: either there is not enough current
+ * data to make a decision or the battery is currently discharging.
+ *
+ * @return how much time is left, in milliseconds, until the battery is fully charged or -1 if
+ * the computation fails
+ */
+ public long computeChargeTimeRemaining() {
+ try {
+ return mBatteryStats.computeChargeTimeRemaining();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 6ebb102..1d232bf 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3814,6 +3814,9 @@
final BatterySipper bs = sippers.get(i);
String label;
switch (bs.drainType) {
+ case AMBIENT_DISPLAY:
+ label = "ambi";
+ break;
case IDLE:
label="idle";
break;
@@ -4975,6 +4978,9 @@
final BatterySipper bs = sippers.get(i);
pw.print(prefix);
switch (bs.drainType) {
+ case AMBIENT_DISPLAY:
+ pw.print(" Ambient display: ");
+ break;
case IDLE:
pw.print(" Idle: ");
break;
@@ -7777,6 +7783,9 @@
int n = SystemProto.PowerUseItem.UNKNOWN_SIPPER;
int uid = 0;
switch (bs.drainType) {
+ case AMBIENT_DISPLAY:
+ n = SystemProto.PowerUseItem.AMBIENT_DISPLAY;
+ break;
case IDLE:
n = SystemProto.PowerUseItem.IDLE;
break;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 8378a82..7162b8a 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -907,6 +907,8 @@
* <li>{@link android.app.Service#startForeground Service.startForeground} requires
* that apps hold the permission
* {@link android.Manifest.permission#FOREGROUND_SERVICE}.</li>
+ * <li>{@link android.widget.LinearLayout} will always remeasure weighted children,
+ * even if there is no excess space.</li>
* </ul>
*/
public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
diff --git a/core/java/android/os/EventLogTags.logtags b/core/java/android/os/EventLogTags.logtags
new file mode 100644
index 0000000..b143a74
--- /dev/null
+++ b/core/java/android/os/EventLogTags.logtags
@@ -0,0 +1,6 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package android.os
+
+230000 service_manager_stats (call_count|1),(total_time|1|3),(duration|1|3)
+230001 service_manager_slow (time|1|3),(service|3)
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 2a68714..3b32c52 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -77,45 +77,51 @@
/**
* Fetches data for the specified configuration key. Returns a byte array representing proto
* wire-encoded of ConfigMetricsReportList.
+ *
+ * Requires Manifest.permission.DUMP.
*/
byte[] getData(in long key);
/**
* Fetches metadata across statsd. Returns byte array representing wire-encoded proto.
+ *
+ * Requires Manifest.permission.DUMP.
*/
byte[] getMetadata();
/**
* Sets a configuration with the specified config key and subscribes to updates for this
* configuration key. Broadcasts will be sent if this configuration needs to be collected.
- * The configuration must be a wire-encoded StatsDConfig. The receiver for this data is
+ * The configuration must be a wire-encoded StatsdConfig. The receiver for this data is
* registered in a separate function.
*
- * Returns if this configuration was correctly registered.
+ * Requires Manifest.permission.DUMP.
*/
- boolean addConfiguration(in long configKey, in byte[] config);
+ void addConfiguration(in long configKey, in byte[] config);
/**
* Registers the given pending intent for this config key. This intent is invoked when the
* memory consumed by the metrics for this configuration approach the pre-defined limits. There
* can be at most one listener per config key.
*
- * Returns if this listener was correctly registered.
+ * Requires Manifest.permission.DUMP.
*/
- boolean setDataFetchOperation(long configKey, in IBinder intentSender);
+ void setDataFetchOperation(long configKey, in IBinder intentSender);
/**
* Removes the data fetch operation for the specified configuration.
+ *
+ * Requires Manifest.permission.DUMP.
*/
- boolean removeDataFetchOperation(long configKey);
+ void removeDataFetchOperation(long configKey);
/**
* Removes the configuration with the matching config key. No-op if this config key does not
* exist.
*
- * Returns if this configuration key was removed.
+ * Requires Manifest.permission.DUMP.
*/
- boolean removeConfiguration(in long configKey);
+ void removeConfiguration(in long configKey);
/**
* Set the IIntentSender (i.e. PendingIntent) to be used when broadcasting subscriber
@@ -133,16 +139,16 @@
* intentSender must be convertible into an IntentSender using IntentSender(IBinder)
* and cannot be null.
*
- * Returns true if successful.
+ * Requires Manifest.permission.DUMP.
*/
- boolean setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender);
+ void setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender);
/**
* Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
* Any broadcasts associated with subscriberId will henceforth not be sent.
* No-op if this (configKey, subsriberId) pair was not associated with an IntentSender.
*
- * Returns true if successful.
+ * Requires Manifest.permission.DUMP.
*/
- boolean unsetBroadcastSubscriber(long configKey, long subscriberId);
+ void unsetBroadcastSubscriber(long configKey, long subscriberId);
}
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 96e7a59..b1c33c2 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -254,6 +254,7 @@
} else if (record != null) {
record.mEvents = 0;
mFileDescriptorRecords.removeAt(index);
+ nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
}
}
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 3be76d6..165276d 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -18,7 +18,9 @@
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BinderInternal;
+import com.android.internal.util.StatLogger;
import java.util.HashMap;
import java.util.Map;
@@ -26,9 +28,76 @@
/** @hide */
public final class ServiceManager {
private static final String TAG = "ServiceManager";
+ private static final Object sLock = new Object();
+
private static IServiceManager sServiceManager;
+
+ /**
+ * Cache for the "well known" services, such as WM and AM.
+ */
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
+ /**
+ * We do the "slow log" at most once every this interval.
+ */
+ private static final int SLOW_LOG_INTERVAL_MS = 5000;
+
+ /**
+ * We do the "stats log" at most once every this interval.
+ */
+ private static final int STATS_LOG_INTERVAL_MS = 5000;
+
+ /**
+ * Threshold in uS for a "slow" call, used on core UIDs. We use a more relax value to
+ * avoid logspam.
+ */
+ private static final long GET_SERVICE_SLOW_THRESHOLD_US_CORE =
+ SystemProperties.getInt("debug.servicemanager.slow_call_core_ms", 10) * 1000;
+
+ /**
+ * Threshold in uS for a "slow" call, used on non-core UIDs. We use a more relax value to
+ * avoid logspam.
+ */
+ private static final long GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE =
+ SystemProperties.getInt("debug.servicemanager.slow_call_ms", 50) * 1000;
+
+ /**
+ * We log stats logging ever this many getService() calls.
+ */
+ private static final int GET_SERVICE_LOG_EVERY_CALLS_CORE =
+ SystemProperties.getInt("debug.servicemanager.log_calls_core", 100);
+
+ /**
+ * We log stats logging ever this many getService() calls.
+ */
+ private static final int GET_SERVICE_LOG_EVERY_CALLS_NON_CORE =
+ SystemProperties.getInt("debug.servicemanager.log_calls", 200);
+
+ @GuardedBy("sLock")
+ private static int sGetServiceAccumulatedUs;
+
+ @GuardedBy("sLock")
+ private static int sGetServiceAccumulatedCallCount;
+
+ @GuardedBy("sLock")
+ private static long sLastStatsLogUptime;
+
+ @GuardedBy("sLock")
+ private static long sLastSlowLogUptime;
+
+ @GuardedBy("sLock")
+ private static long sLastSlowLogActualTime;
+
+ interface Stats {
+ int GET_SERVICE = 0;
+
+ int COUNT = GET_SERVICE + 1;
+ }
+
+ public static final StatLogger sStatLogger = new StatLogger(new String[] {
+ "getService()",
+ });
+
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
@@ -52,7 +121,7 @@
if (service != null) {
return service;
} else {
- return Binder.allowBlocking(getIServiceManager().getService(name));
+ return Binder.allowBlocking(rawGetService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
@@ -177,4 +246,59 @@
super("No service published for: " + name);
}
}
+
+ private static IBinder rawGetService(String name) throws RemoteException {
+ final long start = sStatLogger.getTime();
+
+ final IBinder binder = getIServiceManager().getService(name);
+
+ final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);
+
+ final int myUid = Process.myUid();
+ final boolean isCore = UserHandle.isCore(myUid);
+
+ final long slowThreshold = isCore
+ ? GET_SERVICE_SLOW_THRESHOLD_US_CORE
+ : GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE;
+
+ synchronized (sLock) {
+ sGetServiceAccumulatedUs += time;
+ sGetServiceAccumulatedCallCount++;
+
+ final long nowUptime = SystemClock.uptimeMillis();
+
+ // Was a slow call?
+ if (time >= slowThreshold) {
+ // We do a slow log:
+ // - At most once in every SLOW_LOG_INTERVAL_MS
+ // - OR it was slower than the previously logged slow call.
+ if ((nowUptime > (sLastSlowLogUptime + SLOW_LOG_INTERVAL_MS))
+ || (sLastSlowLogActualTime < time)) {
+ EventLogTags.writeServiceManagerSlow(time / 1000, name);
+
+ sLastSlowLogUptime = nowUptime;
+ sLastSlowLogActualTime = time;
+ }
+ }
+
+ // Every GET_SERVICE_LOG_EVERY_CALLS calls, log the total time spent in getService().
+
+ final int logInterval = isCore
+ ? GET_SERVICE_LOG_EVERY_CALLS_CORE
+ : GET_SERVICE_LOG_EVERY_CALLS_NON_CORE;
+
+ if ((sGetServiceAccumulatedCallCount >= logInterval)
+ && (nowUptime >= (sLastStatsLogUptime + STATS_LOG_INTERVAL_MS))) {
+
+ EventLogTags.writeServiceManagerStats(
+ sGetServiceAccumulatedCallCount, // Total # of getService() calls.
+ sGetServiceAccumulatedUs / 1000, // Total time spent in getService() calls.
+ (int) (nowUptime - sLastStatsLogUptime)); // Uptime duration since last log.
+ sGetServiceAccumulatedCallCount = 0;
+ sGetServiceAccumulatedUs = 0;
+ sLastStatsLogUptime = nowUptime;
+ }
+ }
+ return binder;
+ }
}
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 17d83db..3270719 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -924,13 +924,17 @@
/** @hide */
@VisibleForTesting
public int[] getUids() {
- return mUids;
+ int[] uids = new int[mSize];
+ System.arraycopy(mUids, 0, uids, 0, mSize);
+ return uids;
}
/** @hide */
@VisibleForTesting
public String[] getTags() {
- return mTags;
+ String[] tags = new String[mSize];
+ System.arraycopy(mTags, 0, tags, 0, mSize);
+ return tags;
}
/** @hide */
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index d1d5d8e..673da50 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -166,6 +166,11 @@
private List<String> mApiBlacklistExemptions = Collections.emptyList();
/**
+ * Proportion of hidden API accesses that should be logged to the event log; 0 - 0x10000.
+ */
+ private int mHiddenApiAccessLogSampleRate;
+
+ /**
* The state of the connection to the primary zygote.
*/
private ZygoteState primaryZygoteState;
@@ -478,6 +483,21 @@
}
}
+ /**
+ * Set the precentage of detected hidden API accesses that are logged to the event log.
+ *
+ * <p>This rate will take affect for all new processes forked from the zygote after this call.
+ *
+ * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging.
+ */
+ public void setHiddenApiAccessLogSampleRate(int rate) {
+ synchronized (mLock) {
+ mHiddenApiAccessLogSampleRate = rate;
+ maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
+ maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
+ }
+ }
+
@GuardedBy("mLock")
private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
if (state == null || state.isClosed()) {
@@ -505,6 +525,29 @@
}
}
+ private void maybeSetHiddenApiAccessLogSampleRate(ZygoteState state) {
+ if (state == null || state.isClosed()) {
+ return;
+ }
+ if (mHiddenApiAccessLogSampleRate == -1) {
+ return;
+ }
+ try {
+ state.writer.write(Integer.toString(1));
+ state.writer.newLine();
+ state.writer.write("--hidden-api-log-sampling-rate="
+ + Integer.toString(mHiddenApiAccessLogSampleRate));
+ state.writer.newLine();
+ state.writer.flush();
+ int status = state.inputStream.readInt();
+ if (status != 0) {
+ Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate; status " + status);
+ }
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate", ioe);
+ }
+ }
+
/**
* Tries to open socket to Zygote process if not already open. If
* already open, does nothing. May block and retry. Requires that mLock be held.
@@ -520,6 +563,7 @@
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
maybeSetApiBlacklistExemptions(primaryZygoteState, false);
+ maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
}
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
@@ -533,6 +577,7 @@
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
+ maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
}
if (secondaryZygoteState.matches(abi)) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c7c2c15..5b7adf0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11766,6 +11766,15 @@
"hidden_api_blacklist_exemptions";
/**
+ * Sampling rate for hidden API access event logs, as an integer in the range 0 to 0x10000
+ * inclusive.
+ *
+ * @hide
+ */
+ public static final String HIDDEN_API_ACCESS_LOG_SAMPLING_RATE =
+ "hidden_api_access_log_sampling_rate";
+
+ /**
* Hidden API enforcement policy for apps targeting SDK versions prior to the latest
* version.
*
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index f351c5a..b84843b 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -288,6 +289,18 @@
}
/**
+ * Checks whether the recoverable key store is currently available.
+ *
+ * <p>If it returns true, the device must currently be using a screen lock that is supported for
+ * use with the recoverable key store, i.e. AOSP PIN, pattern or password.
+ */
+ @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+ public static boolean isRecoverableKeyStoreEnabled(@NonNull Context context) {
+ KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+ return keyguardManager != null && keyguardManager.isDeviceSecure();
+ }
+
+ /**
* @deprecated Use {@link #initRecoveryService(String, byte[], byte[])} instead.
*/
@Deprecated
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index daecea7..7b01f7a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -83,7 +83,8 @@
private static final int DAY_MINUTES = 24 * 60;
private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
- // Default allow categories set in readXml() from default_zen_mode_config.xml, fallback values:
+ // Default allow categories set in readXml() from default_zen_mode_config.xml,
+ // fallback/upgrade values:
private static final boolean DEFAULT_ALLOW_ALARMS = true;
private static final boolean DEFAULT_ALLOW_MEDIA = true;
private static final boolean DEFAULT_ALLOW_SYSTEM = false;
@@ -97,7 +98,7 @@
private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
Policy.getAllSuppressedVisualEffects();
- public static final int XML_VERSION = 6;
+ public static final int XML_VERSION = 7;
public static final String ZEN_TAG = "zen";
private static final String ZEN_ATT_VERSION = "version";
private static final String ZEN_ATT_USER = "user";
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a132730..7f75f0a 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -813,7 +813,7 @@
}
final int relayoutResult = mSession.relayout(
mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
- View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
+ View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
diff --git a/core/java/android/util/RecurrenceRule.java b/core/java/android/util/RecurrenceRule.java
index 975ad48..9c89876 100644
--- a/core/java/android/util/RecurrenceRule.java
+++ b/core/java/android/util/RecurrenceRule.java
@@ -149,6 +149,10 @@
}
};
+ public boolean isRecurring() {
+ return period != null;
+ }
+
@Deprecated
public boolean isMonthly() {
return start != null
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 66a9c6c..f59c0b5 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -31,6 +31,7 @@
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.util.PathParser;
import android.util.proto.ProtoOutputStream;
@@ -75,15 +76,19 @@
false /* copyArguments */);
+ private static final Pair<Path, DisplayCutout> NULL_PAIR = new Pair<>(null, null);
private static final Object CACHE_LOCK = new Object();
+
@GuardedBy("CACHE_LOCK")
private static String sCachedSpec;
@GuardedBy("CACHE_LOCK")
private static int sCachedDisplayWidth;
@GuardedBy("CACHE_LOCK")
+ private static int sCachedDisplayHeight;
+ @GuardedBy("CACHE_LOCK")
private static float sCachedDensity;
@GuardedBy("CACHE_LOCK")
- private static DisplayCutout sCachedCutout;
+ private static Pair<Path, DisplayCutout> sCachedCutout = NULL_PAIR;
private final Rect mSafeInsets;
private final Region mBounds;
@@ -347,7 +352,7 @@
}
/**
- * Creates an instance according to @android:string/config_mainBuiltInDisplayCutout.
+ * Creates the bounding path according to @android:string/config_mainBuiltInDisplayCutout.
*
* @hide
*/
@@ -357,6 +362,16 @@
}
/**
+ * Creates an instance according to @android:string/config_mainBuiltInDisplayCutout.
+ *
+ * @hide
+ */
+ public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) {
+ return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+ displayWidth, displayHeight, res.getDisplayMetrics().density).first;
+ }
+
+ /**
* Creates an instance according to the supplied {@link android.util.PathParser.PathData} spec.
*
* @hide
@@ -364,11 +379,17 @@
@VisibleForTesting(visibility = PRIVATE)
public static DisplayCutout fromSpec(String spec, int displayWidth, int displayHeight,
float density) {
+ return pathAndDisplayCutoutFromSpec(spec, displayWidth, displayHeight, density).second;
+ }
+
+ private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(String spec,
+ int displayWidth, int displayHeight, float density) {
if (TextUtils.isEmpty(spec)) {
- return null;
+ return NULL_PAIR;
}
synchronized (CACHE_LOCK) {
if (spec.equals(sCachedSpec) && sCachedDisplayWidth == displayWidth
+ && sCachedDisplayHeight == displayHeight
&& sCachedDensity == density) {
return sCachedCutout;
}
@@ -398,7 +419,7 @@
p = PathParser.createPathFromPathData(spec);
} catch (Throwable e) {
Log.wtf(TAG, "Could not inflate cutout: ", e);
- return null;
+ return NULL_PAIR;
}
final Matrix m = new Matrix();
@@ -414,7 +435,7 @@
bottomPath = PathParser.createPathFromPathData(bottomSpec);
} catch (Throwable e) {
Log.wtf(TAG, "Could not inflate bottom cutout: ", e);
- return null;
+ return NULL_PAIR;
}
// Keep top transform
m.postTranslate(0, displayHeight);
@@ -422,10 +443,11 @@
p.addPath(bottomPath);
}
- final DisplayCutout result = fromBounds(p);
+ final Pair<Path, DisplayCutout> result = new Pair<>(p, fromBounds(p));
synchronized (CACHE_LOCK) {
sCachedSpec = spec;
sCachedDisplayWidth = displayWidth;
+ sCachedDisplayHeight = displayHeight;
sCachedDensity = density;
sCachedCutout = result;
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index d8a5609..f868a00 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -66,6 +66,7 @@
* @param viewVisibility Window root view's visibility.
* @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING},
* {@link WindowManagerGlobal#RELAYOUT_DEFER_SURFACE_DESTROY}.
+ * @param frameNumber A frame number in which changes requested in this layout will be rendered.
* @param outFrame Rect in which is placed the new position/size on
* screen.
* @param outOverscanInsets Rect in which is placed the offsets from
@@ -96,7 +97,7 @@
*/
int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility,
- int flags, out Rect outFrame, out Rect outOverscanInsets,
+ int flags, long frameNumber, out Rect outFrame, out Rect outOverscanInsets,
out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
out Rect outOutsets, out Rect outBackdropFrame,
out DisplayCutout.ParcelableWrapper displayCutout,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index dc58f11..b367dc7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11791,6 +11791,14 @@
return null;
}
+ /** @hide */
+ View getSelfOrParentImportantForA11y() {
+ if (isImportantForAccessibility()) return this;
+ ViewParent parent = getParentForAccessibility();
+ if (parent instanceof View) return (View) parent;
+ return null;
+ }
+
/**
* Adds the children of this View relevant for accessibility to the given list
* as output. Since some Views are not important for accessibility the added
@@ -15006,10 +15014,7 @@
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mAlpha != alpha) {
- // Report visibility changes, which can affect children, to accessibility
- if ((alpha == 0) ^ (mTransformationInfo.mAlpha == 0)) {
- notifySubtreeAccessibilityStateChangedIfNeeded();
- }
+ float oldAlpha = mTransformationInfo.mAlpha;
mTransformationInfo.mAlpha = alpha;
if (onSetAlpha((int) (alpha * 255))) {
mPrivateFlags |= PFLAG_ALPHA_SET;
@@ -15021,6 +15026,10 @@
invalidateViewProperty(true, false);
mRenderNode.setAlpha(getFinalAlpha());
}
+ // Report visibility changes, which can affect children, to accessibility
+ if ((alpha == 0) ^ (oldAlpha == 0)) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6002fe5..2ec42c0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5692,6 +5692,7 @@
}
dispatchVisibilityAggregated(isAttachedToWindow() && getWindowVisibility() == VISIBLE
&& isShown());
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e802232..730c372 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6482,17 +6482,17 @@
params.type = mOrigWindowType;
}
}
-
- if (mSurface.isValid()) {
- params.frameNumber = mSurface.getNextFrameNumber();
- }
}
- int relayoutResult = mWindowSession.relayout(
- mWindow, mSeq, params,
+ long frameNumber = -1;
+ if (mSurface.isValid()) {
+ frameNumber = mSurface.getNextFrameNumber();
+ }
+
+ int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
- (int) (mView.getMeasuredHeight() * appScale + 0.5f),
- viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
+ (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
+ insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
mPendingMergedConfiguration, mSurface);
@@ -8305,6 +8305,12 @@
public View mSource;
public long mLastEventTimeMillis;
+ /**
+ * Override for {@link AccessibilityEvent#originStackTrace} to provide the stack trace
+ * of the original {@link #runOrPost} call instead of one for sending the delayed event
+ * from a looper.
+ */
+ public StackTraceElement[] mOrigin;
@Override
public void run() {
@@ -8322,6 +8328,7 @@
AccessibilityEvent event = AccessibilityEvent.obtain();
event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
event.setContentChangeTypes(mChangeTypes);
+ if (AccessibilityEvent.DEBUG_ORIGIN) event.originStackTrace = mOrigin;
source.sendAccessibilityEventUnchecked(event);
} else {
mLastEventTimeMillis = 0;
@@ -8329,6 +8336,7 @@
// In any case reset to initial state.
source.resetSubtreeAccessibilityStateChanged();
mChangeTypes = 0;
+ if (AccessibilityEvent.DEBUG_ORIGIN) mOrigin = null;
}
public void runOrPost(View source, int changeType) {
@@ -8352,12 +8360,18 @@
// If there is no common predecessor, then mSource points to
// a removed view, hence in this case always prefer the source.
View predecessor = getCommonPredecessor(mSource, source);
+ if (predecessor != null) {
+ predecessor = predecessor.getSelfOrParentImportantForA11y();
+ }
mSource = (predecessor != null) ? predecessor : source;
mChangeTypes |= changeType;
return;
}
mSource = source;
mChangeTypes = changeType;
+ if (AccessibilityEvent.DEBUG_ORIGIN) {
+ mOrigin = Thread.currentThread().getStackTrace();
+ }
final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis;
final long minEventIntevalMillis =
ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index f6181d7..0f5c23f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2438,13 +2438,6 @@
public long hideTimeoutMilliseconds = -1;
/**
- * A frame number in which changes requested in this layout will be rendered.
- *
- * @hide
- */
- public long frameNumber = -1;
-
- /**
* The color mode requested by this window. The target display may
* not be able to honor the request. When the color mode is not set
* to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the
@@ -2617,7 +2610,6 @@
TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
out.writeInt(mColorMode);
out.writeLong(hideTimeoutMilliseconds);
- out.writeLong(frameNumber);
}
public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -2674,7 +2666,6 @@
accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mColorMode = in.readInt();
hideTimeoutMilliseconds = in.readLong();
- frameNumber = in.readLong();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2875,10 +2866,6 @@
changes |= SURFACE_INSETS_CHANGED;
}
- // The frame number changing is only relevant in the context of other
- // changes, and so we don't need to track it with a flag.
- frameNumber = o.frameNumber;
-
if (hasManualSurfaceInsets != o.hasManualSurfaceInsets) {
hasManualSurfaceInsets = o.hasManualSurfaceInsets;
changes |= SURFACE_INSETS_CHANGED;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index e0f74a7..7946e9e 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -201,6 +201,7 @@
* <em>Properties:</em></br>
* <ul>
* <li>{@link #getEventType()} - The type of the event.</li>
+ * <li>{@link #getContentChangeTypes()} - The type of state changes.</li>
* <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
@@ -388,6 +389,8 @@
*/
public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable {
private static final boolean DEBUG = false;
+ /** @hide */
+ public static final boolean DEBUG_ORIGIN = false;
/**
* Invalid selection/focus position.
@@ -748,7 +751,7 @@
private static final int MAX_POOL_SIZE = 10;
private static final SynchronizedPool<AccessibilityEvent> sPool =
- new SynchronizedPool<AccessibilityEvent>(MAX_POOL_SIZE);
+ new SynchronizedPool<>(MAX_POOL_SIZE);
private @EventType int mEventType;
private CharSequence mPackageName;
@@ -758,6 +761,17 @@
int mContentChangeTypes;
int mWindowChangeTypes;
+ /**
+ * The stack trace describing where this event originated from on the app side.
+ * Only populated if {@link #DEBUG_ORIGIN} is enabled
+ * Can be inspected(e.g. printed) from an
+ * {@link android.accessibilityservice.AccessibilityService} to trace where particular events
+ * are being dispatched from.
+ *
+ * @hide
+ */
+ public StackTraceElement[] originStackTrace = null;
+
private ArrayList<AccessibilityRecord> mRecords;
/*
@@ -780,6 +794,7 @@
mWindowChangeTypes = event.mWindowChangeTypes;
mEventTime = event.mEventTime;
mPackageName = event.mPackageName;
+ if (DEBUG_ORIGIN) originStackTrace = event.originStackTrace;
}
/**
@@ -849,16 +864,17 @@
}
/**
- * Gets the bit mask of change types signaled by an
- * {@link #TYPE_WINDOW_CONTENT_CHANGED} event. A single event may represent
- * multiple change types.
+ * Gets the bit mask of change types signaled by a
+ * {@link #TYPE_WINDOW_CONTENT_CHANGED} event or {@link #TYPE_WINDOW_STATE_CHANGED}. A single
+ * event may represent multiple change types.
*
* @return The bit mask of change types. One or more of:
* <ul>
- * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
- * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
- * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
- * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
+ * <li>{@link #CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
+ * <li>{@link #CONTENT_CHANGE_TYPE_SUBTREE}
+ * <li>{@link #CONTENT_CHANGE_TYPE_TEXT}
+ * <li>{@link #CONTENT_CHANGE_TYPE_PANE_TITLE}
+ * <li>{@link #CONTENT_CHANGE_TYPE_UNDEFINED}
* </ul>
*/
@ContentChangeTypes
@@ -877,6 +893,7 @@
}
case CONTENT_CHANGE_TYPE_SUBTREE: return "CONTENT_CHANGE_TYPE_SUBTREE";
case CONTENT_CHANGE_TYPE_TEXT: return "CONTENT_CHANGE_TYPE_TEXT";
+ case CONTENT_CHANGE_TYPE_PANE_TITLE: return "CONTENT_CHANGE_TYPE_PANE_TITLE";
case CONTENT_CHANGE_TYPE_UNDEFINED: return "CONTENT_CHANGE_TYPE_UNDEFINED";
default: return Integer.toHexString(type);
}
@@ -1104,7 +1121,9 @@
*/
public static AccessibilityEvent obtain() {
AccessibilityEvent event = sPool.acquire();
- return (event != null) ? event : new AccessibilityEvent();
+ if (event == null) event = new AccessibilityEvent();
+ if (DEBUG_ORIGIN) event.originStackTrace = Thread.currentThread().getStackTrace();
+ return event;
}
/**
@@ -1142,6 +1161,7 @@
record.recycle();
}
}
+ if (DEBUG_ORIGIN) originStackTrace = null;
}
/**
@@ -1164,7 +1184,7 @@
// Read the records.
final int recordCount = parcel.readInt();
if (recordCount > 0) {
- mRecords = new ArrayList<AccessibilityRecord>(recordCount);
+ mRecords = new ArrayList<>(recordCount);
for (int i = 0; i < recordCount; i++) {
AccessibilityRecord record = AccessibilityRecord.obtain();
readAccessibilityRecordFromParcel(record, parcel);
@@ -1172,6 +1192,17 @@
mRecords.add(record);
}
}
+
+ if (DEBUG_ORIGIN) {
+ originStackTrace = new StackTraceElement[parcel.readInt()];
+ for (int i = 0; i < originStackTrace.length; i++) {
+ originStackTrace[i] = new StackTraceElement(
+ parcel.readString(),
+ parcel.readString(),
+ parcel.readString(),
+ parcel.readInt());
+ }
+ }
}
/**
@@ -1227,6 +1258,17 @@
AccessibilityRecord record = mRecords.get(i);
writeAccessibilityRecordToParcel(record, parcel, flags);
}
+
+ if (DEBUG_ORIGIN) {
+ if (originStackTrace == null) originStackTrace = Thread.currentThread().getStackTrace();
+ parcel.writeInt(originStackTrace.length);
+ for (StackTraceElement element : originStackTrace) {
+ parcel.writeString(element.getClassName());
+ parcel.writeString(element.getMethodName());
+ parcel.writeString(element.getFileName());
+ parcel.writeInt(element.getLineNumber());
+ }
+ }
}
/**
@@ -1285,7 +1327,7 @@
}
if (!DEBUG_CONCISE_TOSTRING || mWindowChangeTypes != 0) {
builder.append("; WindowChangeTypes: ").append(
- contentChangeTypesToString(mWindowChangeTypes));
+ windowChangeTypesToString(mWindowChangeTypes));
}
super.appendTo(builder);
if (DEBUG || DEBUG_CONCISE_TOSTRING) {
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 72af203..d60c481 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -326,12 +326,14 @@
accessibilityWindowId, accessibilityNodeId);
if (cachedInfo != null) {
if (DEBUG) {
- Log.i(LOG_TAG, "Node cache hit");
+ Log.i(LOG_TAG, "Node cache hit for "
+ + idToString(accessibilityWindowId, accessibilityNodeId));
}
return cachedInfo;
}
if (DEBUG) {
- Log.i(LOG_TAG, "Node cache miss");
+ Log.i(LOG_TAG, "Node cache miss for "
+ + idToString(accessibilityWindowId, accessibilityNodeId));
}
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
@@ -368,6 +370,11 @@
return null;
}
+ private static String idToString(int accessibilityWindowId, long accessibilityNodeId) {
+ return accessibilityWindowId + "/"
+ + AccessibilityNodeInfo.idToString(accessibilityNodeId);
+ }
+
/**
* Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in
* the window whose id is specified and starts from the node whose accessibility
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index dee267d..cbb23f1 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -16,6 +16,8 @@
package android.view.accessibility;
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
+
import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
@@ -44,6 +46,7 @@
import android.view.IWindow;
import android.view.View;
import android.view.accessibility.AccessibilityEvent.EventType;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IntPair;
@@ -51,8 +54,6 @@
import java.util.Collections;
import java.util.List;
-import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
-
/**
* System level service that serves as an event dispatch for {@link AccessibilityEvent}s,
* and provides facilities for querying the accessibility state of the system.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 4c437dd..03f1c12 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3874,6 +3874,24 @@
| FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null);
}
+ /** @hide */
+ public static String idToString(long accessibilityId) {
+ int accessibilityViewId = getAccessibilityViewId(accessibilityId);
+ int virtualDescendantId = getVirtualDescendantId(accessibilityId);
+ return virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID
+ ? idItemToString(accessibilityViewId)
+ : idItemToString(accessibilityViewId) + ":" + idItemToString(virtualDescendantId);
+ }
+
+ private static String idItemToString(int item) {
+ switch (item) {
+ case ROOT_ITEM_ID: return "ROOT";
+ case UNDEFINED_ITEM_ID: return "UNDEFINED";
+ case AccessibilityNodeProvider.HOST_VIEW_ID: return "HOST";
+ default: return "" + item;
+ }
+ }
+
/**
* A class defining an action that can be performed on an {@link AccessibilityNodeInfo}.
* Each action has a unique id that is mandatory and optional data.
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index f80625f..96016b4 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -375,13 +375,13 @@
*/
public static final class Builder {
- @NonNull private String mText;
@NonNull private List<RemoteAction> mActions = new ArrayList<>();
@NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
- @Nullable Drawable mLegacyIcon;
- @Nullable String mLegacyLabel;
- @Nullable Intent mLegacyIntent;
- @Nullable OnClickListener mLegacyOnClickListener;
+ @Nullable private String mText;
+ @Nullable private Drawable mLegacyIcon;
+ @Nullable private String mLegacyLabel;
+ @Nullable private Intent mLegacyIntent;
+ @Nullable private OnClickListener mLegacyOnClickListener;
@Nullable private String mId;
/**
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 3d503e2..851b2c9 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -339,7 +339,7 @@
/**
* @return The config representing the set of entities to look for
- * @see #setEntityConfig(TextClassifier.EntityConfig)
+ * @see Builder#setEntityConfig(TextClassifier.EntityConfig)
*/
@Nullable
public TextClassifier.EntityConfig getEntityConfig() {
diff --git a/core/java/android/webkit/TracingConfig.java b/core/java/android/webkit/TracingConfig.java
index d95ca61..2080168 100644
--- a/core/java/android/webkit/TracingConfig.java
+++ b/core/java/android/webkit/TracingConfig.java
@@ -54,37 +54,37 @@
/**
* Predefined set of categories typically useful for analyzing WebViews.
- * Typically includes android_webview and Java.
+ * Typically includes "android_webview" and "Java" categories.
*/
public static final int CATEGORIES_ANDROID_WEBVIEW = 1 << 1;
/**
* Predefined set of categories typically useful for web developers.
- * Typically includes blink, compositor, renderer.scheduler and v8 categories.
+ * Typically includes "blink", "compositor", "renderer.scheduler" and "v8" categories.
*/
public static final int CATEGORIES_WEB_DEVELOPER = 1 << 2;
/**
* Predefined set of categories for analyzing input latency issues.
- * Typically includes input, renderer.scheduler categories.
+ * Typically includes "input", "renderer.scheduler" categories.
*/
public static final int CATEGORIES_INPUT_LATENCY = 1 << 3;
/**
* Predefined set of categories for analyzing rendering issues.
- * Typically includes blink, compositor and gpu categories.
+ * Typically includes "blink", "compositor" and "gpu" categories.
*/
public static final int CATEGORIES_RENDERING = 1 << 4;
/**
* Predefined set of categories for analyzing javascript and rendering issues.
- * Typically includes blink, compositor, gpu, renderer.scheduler and v8 categories.
+ * Typically includes "blink", "compositor", "gpu", "renderer.scheduler" and "v8" categories.
*/
public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 1 << 5;
/**
* Predefined set of categories for studying difficult rendering performance problems.
- * Typically includes blink, compositor, gpu, renderer.scheduler, v8 and
+ * Typically includes "blink", "compositor", "gpu", "renderer.scheduler", "v8" and
* some other compositor categories which are disabled by default.
*/
public static final int CATEGORIES_FRAME_VIEWER = 1 << 6;
@@ -123,7 +123,9 @@
}
/**
- * Returns a bitmask of the predefined categories values of this configuration.
+ * Returns a bitmask of the predefined category sets of this configuration.
+ *
+ * @return Bitmask of predefined category sets.
*/
@PredefinedCategories
public int getPredefinedCategories() {
@@ -133,7 +135,7 @@
/**
* Returns the list of included custom category patterns for this configuration.
*
- * @return empty list if no custom category patterns are specified.
+ * @return Empty list if no custom category patterns are specified.
*/
@NonNull
public List<String> getCustomIncludedCategories() {
@@ -142,6 +144,8 @@
/**
* Returns the tracing mode of this configuration.
+ *
+ * @return The tracing mode of this configuration.
*/
@TracingMode
public int getTracingMode() {
@@ -150,28 +154,37 @@
/**
* Builder used to create {@link TracingConfig} objects.
- *
+ * <p>
* Examples:
- * new TracingConfig.Builder().build()
- * -- creates a configuration with default options: {@link #CATEGORIES_NONE},
- * {@link #RECORD_UNTIL_FULL}.
- * new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER).build()
- * -- records trace events from the "web developer" predefined category sets.
- * new TracingConfig.Builder().addCategories(CATEGORIES_RENDERING,
- * CATEGORIES_INPUT_LATENCY).build()
- * -- records trace events from the "rendering" and "input latency" predefined
- * category sets.
- * new TracingConfig.Builder().addCategories("browser").build()
- * -- records only the trace events from the "browser" category.
- * new TracingConfig.Builder().addCategories("blink*","renderer*").build()
- * -- records only the trace events matching the "blink*" and "renderer*" patterns
- * (e.g. "blink.animations", "renderer_host" and "renderer.scheduler" categories).
- * new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER)
+ * <pre class="prettyprint">
+ * // Create a configuration with default options: {@link #CATEGORIES_NONE},
+ * // {@link #RECORD_CONTINUOUSLY}.
+ * <code>new TracingConfig.Builder().build()</code>
+ *
+ * // Record trace events from the "web developer" predefined category sets.
+ * // Uses a ring buffer (the default {@link #RECORD_CONTINUOUSLY} mode) for
+ * // internal storage during tracing.
+ * <code>new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER).build()</code>
+ *
+ * // Record trace events from the "rendering" and "input latency" predefined
+ * // category sets.
+ * <code>new TracingConfig.Builder().addCategories(CATEGORIES_RENDERING,
+ * CATEGORIES_INPUT_LATENCY).build()</code>
+ *
+ * // Record only the trace events from the "browser" category.
+ * <code>new TracingConfig.Builder().addCategories("browser").build()</code>
+ *
+ * // Record only the trace events matching the "blink*" and "renderer*" patterns
+ * // (e.g. "blink.animations", "renderer_host" and "renderer.scheduler" categories).
+ * <code>new TracingConfig.Builder().addCategories("blink*","renderer*").build()</code>
+ *
+ * // Record events from the "web developer" predefined category set and events from
+ * // the "disabled-by-default-v8.gc" category to understand where garbage collection
+ * // is being triggered. Uses a limited size buffer for internal storage during tracing.
+ * <code>new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER)
* .addCategories("disabled-by-default-v8.gc")
- * .setTracingMode(RECORD_CONTINUOUSLY).build()
- * -- records events from the "web developer" predefined category set and events from
- * the "disabled-by-default-v8.gc" category to understand where garbage collection
- * is being triggered. Uses a ring buffer for internal storage during tracing.
+ * .setTracingMode(RECORD_UNTIL_FULL).build()</code>
+ * </pre>
*/
public static class Builder {
private @PredefinedCategories int mPredefinedCategories = CATEGORIES_NONE;
@@ -185,6 +198,8 @@
/**
* Build {@link TracingConfig} using the current settings.
+ *
+ * @return The {@link TracingConfig} with the current settings.
*/
public TracingConfig build() {
return new TracingConfig(mPredefinedCategories, mCustomIncludedCategories,
@@ -192,16 +207,15 @@
}
/**
- * Adds categories from a predefined set of categories to be included in the trace output.
+ * Adds predefined sets of categories to be included in the trace output.
*
- * @param predefinedCategories list or bitmask of predefined category sets to use:
- * {@link #CATEGORIES_NONE}, {@link #CATEGORIES_ALL},
- * {@link #CATEGORIES_ANDROID_WEBVIEW},
- * {@link #CATEGORIES_WEB_DEVELOPER},
- * {@link #CATEGORIES_INPUT_LATENCY},
- * {@link #CATEGORIES_RENDERING},
- * {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
- * {@link #CATEGORIES_FRAME_VIEWER}.
+ * A predefined category set can be one of {@link #CATEGORIES_NONE},
+ * {@link #CATEGORIES_ALL}, {@link #CATEGORIES_ANDROID_WEBVIEW},
+ * {@link #CATEGORIES_WEB_DEVELOPER}, {@link #CATEGORIES_INPUT_LATENCY},
+ * {@link #CATEGORIES_RENDERING}, {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
+ * {@link #CATEGORIES_FRAME_VIEWER}.
+ *
+ * @param predefinedCategories A list or bitmask of predefined category sets.
* @return The builder to facilitate chaining.
*/
public Builder addCategories(@PredefinedCategories int... predefinedCategories) {
@@ -215,11 +229,11 @@
* Adds custom categories to be included in trace output.
*
* Note that the categories are defined by the currently-in-use version of WebView. They
- * live in chromium code and are not part of the Android API. See
+ * live in chromium code and are not part of the Android API.
* See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
* chromium documentation on tracing</a> for more details.
*
- * @param categories a list of category patterns. A category pattern can contain wilcards,
+ * @param categories A list of category patterns. A category pattern can contain wildcards,
* e.g. "blink*" or full category name e.g. "renderer.scheduler".
* @return The builder to facilitate chaining.
*/
@@ -235,7 +249,7 @@
*
* Same as {@link #addCategories(String...)} but allows to pass a Collection as a parameter.
*
- * @param categories a list of category patters.
+ * @param categories A list of category patterns.
* @return The builder to facilitate chaining.
*/
public Builder addCategories(Collection<String> categories) {
@@ -245,8 +259,9 @@
/**
* Sets the tracing mode for this configuration.
+ * When tracingMode is not set explicitly, the default is {@link #RECORD_CONTINUOUSLY}.
*
- * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL} or
+ * @param tracingMode The tracing mode to use, one of {@link #RECORD_UNTIL_FULL} or
* {@link #RECORD_CONTINUOUSLY}.
* @return The builder to facilitate chaining.
*/
diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java
index 50068f5..05c0304 100644
--- a/core/java/android/webkit/TracingController.java
+++ b/core/java/android/webkit/TracingController.java
@@ -35,9 +35,9 @@
* Example usage:
* <pre class="prettyprint">
* TracingController tracingController = TracingController.getInstance();
- * tracingController.start(new TraceConfig.Builder()
+ * tracingController.start(new TracingConfig.Builder()
* .addCategories(CATEGORIES_WEB_DEVELOPER).build());
- * [..]
+ * ...
* tracingController.stop(new FileOutputStream("trace.json"),
* Executors.newSingleThreadExecutor());
* </pre></p>
@@ -49,7 +49,7 @@
* only one TracingController instance for all WebView instances,
* however this restriction may be relaxed in a future Android release.
*
- * @return the default TracingController instance
+ * @return The default TracingController instance.
*/
@NonNull
public static TracingController getInstance() {
@@ -65,8 +65,10 @@
* using an internal buffer and flushed to the outputStream when
* {@link #stop(OutputStream, Executor)} is called.
*
- * @param tracingConfig configuration options to use for tracing
- * @throws IllegalStateException if the system is already tracing.
+ * @param tracingConfig Configuration options to use for tracing.
+ * @throws IllegalStateException If the system is already tracing.
+ * @throws IllegalArgumentException If the configuration is invalid (e.g.
+ * invalid category pattern or invalid tracing mode).
*/
public abstract void start(@NonNull TracingConfig tracingConfig);
@@ -77,17 +79,22 @@
* in chunks by invoking {@link java.io.OutputStream#write(byte[])}. On completion
* the {@link java.io.OutputStream#close()} method is called.
*
- * @param outputStream the output steam the tracing data will be sent to. If null
+ * @param outputStream The output stream the tracing data will be sent to. If null
* the tracing data will be discarded.
- * @param executor the {@link java.util.concurrent.Executor} on which the
- * outputStream #write and #close methods will be invoked.
- * @return false if the system was not tracing at the time of the call, true
- * otherwise.
+ * @param executor The {@link java.util.concurrent.Executor} on which the
+ * outputStream {@link java.io.OutputStream#write(byte[])} and
+ * {@link java.io.OutputStream#close()} methods will be invoked.
+ * @return False if the WebView framework was not tracing at the time of the call,
+ * true otherwise.
*/
public abstract boolean stop(@Nullable OutputStream outputStream,
@NonNull @CallbackExecutor Executor executor);
- /** True if the system is tracing */
+ /**
+ * Returns whether the WebView framework is tracing.
+ *
+ * @return True if tracing is enabled.
+ */
public abstract boolean isTracing();
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 6af678b..dac100a 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -39,6 +39,7 @@
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -4837,14 +4838,48 @@
return true;
}
- private boolean handleOverlapsMagnifier() {
- final int handleY = mContainer.getDecorViewLayoutParams().y;
- final int magnifierBottomWhenAtWindowTop =
- mTextView.getRootWindowInsets().getSystemWindowInsetTop()
- + mMagnifierAnimator.mMagnifier.getHeight();
- return handleY <= magnifierBottomWhenAtWindowTop;
+ private boolean handleOverlapsMagnifier(@NonNull final HandleView handle,
+ @NonNull final Rect magnifierRect) {
+ final PopupWindow window = handle.mContainer;
+ if (!window.hasDecorView()) {
+ return false;
+ }
+ final Rect handleRect = new Rect(
+ window.getDecorViewLayoutParams().x,
+ window.getDecorViewLayoutParams().y,
+ window.getDecorViewLayoutParams().x + window.getContentView().getWidth(),
+ window.getDecorViewLayoutParams().y + window.getContentView().getHeight());
+ return Rect.intersects(handleRect, magnifierRect);
}
+ private @Nullable HandleView getOtherSelectionHandle() {
+ final SelectionModifierCursorController controller = getSelectionController();
+ if (controller == null || !controller.isActive()) {
+ return null;
+ }
+ return controller.mStartHandle != this
+ ? controller.mStartHandle
+ : controller.mEndHandle;
+ }
+
+ private final Magnifier.Callback mHandlesVisibilityCallback = new Magnifier.Callback() {
+ @Override
+ public void onOperationComplete() {
+ final Point magnifierTopLeft = mMagnifierAnimator.mMagnifier.getWindowCoords();
+ if (magnifierTopLeft == null) {
+ return;
+ }
+ final Rect magnifierRect = new Rect(magnifierTopLeft.x, magnifierTopLeft.y,
+ magnifierTopLeft.x + mMagnifierAnimator.mMagnifier.getWidth(),
+ magnifierTopLeft.y + mMagnifierAnimator.mMagnifier.getHeight());
+ setVisible(!handleOverlapsMagnifier(HandleView.this, magnifierRect));
+ final HandleView otherHandle = getOtherSelectionHandle();
+ if (otherHandle != null) {
+ otherHandle.setVisible(!handleOverlapsMagnifier(otherHandle, magnifierRect));
+ }
+ }
+ };
+
protected final void updateMagnifier(@NonNull final MotionEvent event) {
if (mMagnifierAnimator == null) {
return;
@@ -4858,12 +4893,8 @@
mRenderCursorRegardlessTiming = true;
mTextView.invalidateCursorPath();
suspendBlink();
- // Hide handle if it overlaps the magnifier.
- if (handleOverlapsMagnifier()) {
- setVisible(false);
- } else {
- setVisible(true);
- }
+ mMagnifierAnimator.mMagnifier
+ .setOnOperationCompleteCallback(mHandlesVisibilityCallback);
mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
} else {
@@ -4877,6 +4908,10 @@
mRenderCursorRegardlessTiming = false;
resumeBlink();
setVisible(true);
+ final HandleView otherHandle = getOtherSelectionHandle();
+ if (otherHandle != null) {
+ otherHandle.setVisible(true);
+ }
}
}
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index d32e93c..40f9652 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -217,6 +217,17 @@
private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
+ /**
+ * Signals that compatibility booleans have been initialized according to
+ * target SDK versions.
+ */
+ private static boolean sCompatibilityDone = false;
+
+ /**
+ * Behavior change in P; always remeasure weighted children, regardless of excess space.
+ */
+ private static boolean sRemeasureWeightedChildren = true;
+
public LinearLayout(Context context) {
this(context, null);
}
@@ -232,6 +243,15 @@
public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ if (!sCompatibilityDone && context != null) {
+ final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+
+ // Older apps only remeasure non-zero children
+ sRemeasureWeightedChildren = targetSdkVersion >= Build.VERSION_CODES.P;
+
+ sCompatibilityDone = true;
+ }
+
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.LinearLayout, defStyleAttr, defStyleRes);
@@ -917,7 +937,8 @@
// measurement on any children, we need to measure them now.
int remainingExcess = heightSize - mTotalLength
+ (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
- if (skippedMeasure || totalWeight > 0.0f) {
+ if (skippedMeasure
+ || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) {
float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
mTotalLength = 0;
@@ -1300,7 +1321,8 @@
// measurement on any children, we need to measure them now.
int remainingExcess = widthSize - mTotalLength
+ (mAllowInconsistentMeasurement ? 0 : usedExcessSpace);
- if (skippedMeasure || totalWeight > 0.0f) {
+ if (skippedMeasure
+ || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) {
float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 5eb6699..cb362e6 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -233,6 +233,17 @@
return mZoom;
}
+ /**
+ * @hide
+ */
+ @Nullable
+ public Point getWindowCoords() {
+ if (mWindow == null) {
+ return null;
+ }
+ return new Point(mWindow.mLastDrawContentPositionX, mWindow.mLastDrawContentPositionY);
+ }
+
@Nullable
private Surface getValidViewSurface() {
// TODO: deduplicate this against the first part of #performPixelCopy
@@ -374,8 +385,11 @@
private final Runnable mMagnifierUpdater;
// The handler where the magnifier updater jobs will be post'd.
private final Handler mHandler;
- // The callback to be run after the next draw. Only used for testing.
+ // The callback to be run after the next draw.
private Callback mCallback;
+ // The position of the magnifier content when the last draw was requested.
+ private int mLastDrawContentPositionX;
+ private int mLastDrawContentPositionY;
// Members below describe the state of the magnifier. Reads/writes to them
// have to be synchronized between the UI thread and the thread that handles
@@ -598,6 +612,8 @@
callback = null;
}
+ mLastDrawContentPositionX = mWindowPositionX + mOffsetX;
+ mLastDrawContentPositionY = mWindowPositionY + mOffsetY;
mFrameDrawScheduled = false;
}
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 322c876..25af355 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -19,10 +19,10 @@
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Slog;
-import android.view.Window;
import android.view.WindowManager;
import com.android.internal.R;
@@ -31,6 +31,10 @@
implements DialogInterface.OnClickListener {
private static final String TAG = "SuspendedAppActivity";
+ public static final String EXTRA_SUSPENDED_PACKAGE =
+ "SuspendedAppActivity.extra.SUSPENDED_PACKAGE";
+ public static final String EXTRA_SUSPENDING_PACKAGE =
+ "SuspendedAppActivity.extra.SUSPENDING_PACKAGE";
public static final String EXTRA_DIALOG_MESSAGE = "SuspendedAppActivity.extra.DIALOG_MESSAGE";
public static final String EXTRA_MORE_DETAILS_INTENT =
"SuspendedAppActivity.extra.MORE_DETAILS_INTENT";
@@ -38,10 +42,19 @@
private Intent mMoreDetailsIntent;
private int mUserId;
+ private CharSequence getAppLabel(String packageName) {
+ final PackageManager pm = getPackageManager();
+ try {
+ return pm.getApplicationInfoAsUser(packageName, 0, mUserId).loadLabel(pm);
+ } catch (PackageManager.NameNotFoundException ne) {
+ Slog.e(TAG, "Package " + packageName + " not found", ne);
+ }
+ return packageName;
+ }
+
@Override
public void onCreate(Bundle icicle) {
- Window window = getWindow();
- window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
super.onCreate(icicle);
final Intent intent = getIntent();
@@ -52,15 +65,22 @@
finish();
return;
}
- String dialogMessage = intent.getStringExtra(EXTRA_DIALOG_MESSAGE);
- if (dialogMessage == null) {
- dialogMessage = getString(R.string.app_suspended_default_message);
+ final String suppliedMessage = intent.getStringExtra(EXTRA_DIALOG_MESSAGE);
+ final CharSequence suspendedAppLabel = getAppLabel(
+ intent.getStringExtra(EXTRA_SUSPENDED_PACKAGE));
+ final CharSequence dialogMessage;
+ if (suppliedMessage == null) {
+ dialogMessage = getString(R.string.app_suspended_default_message,
+ suspendedAppLabel,
+ getAppLabel(intent.getStringExtra(EXTRA_SUSPENDING_PACKAGE)));
+ } else {
+ dialogMessage = String.format(getResources().getConfiguration().getLocales().get(0),
+ suppliedMessage, suspendedAppLabel);
}
final AlertController.AlertParams ap = mAlertParams;
ap.mTitle = getString(R.string.app_suspended_title);
- ap.mMessage = String.format(getResources().getConfiguration().getLocales().get(0),
- dialogMessage, intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME));
+ ap.mMessage = dialogMessage;
ap.mPositiveButtonText = getString(android.R.string.ok);
if (mMoreDetailsIntent != null) {
ap.mNeutralButtonText = getString(R.string.app_suspended_more_details);
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 5d40a73..f537e3e 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -166,6 +166,11 @@
return null;
}
+ if (parsedArgs.hiddenApiAccessLogSampleRate != -1) {
+ handleHiddenApiAccessLogSampleRate(parsedArgs.hiddenApiAccessLogSampleRate);
+ return null;
+ }
+
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: " +
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -294,6 +299,15 @@
}
}
+ private void handleHiddenApiAccessLogSampleRate(int percent) {
+ try {
+ ZygoteInit.setHiddenApiAccessLogSampleRate(percent);
+ mSocketOutStream.writeInt(0);
+ } catch (IOException ioe) {
+ throw new IllegalStateException("Error writing to command socket", ioe);
+ }
+ }
+
protected void preload() {
ZygoteInit.lazyPreload();
}
@@ -461,6 +475,12 @@
String[] apiBlacklistExemptions;
/**
+ * Sampling rate for logging hidden API accesses to the event log. This is sent to the
+ * pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate.
+ */
+ int hiddenApiAccessLogSampleRate = -1;
+
+ /**
* Constructs instance and parses args
* @param args zygote command-line args
* @throws IllegalArgumentException
@@ -483,6 +503,7 @@
boolean seenRuntimeArgs = false;
+ boolean expectRuntimeArgs = true;
for ( /* curArg */ ; curArg < args.length; curArg++) {
String arg = args[curArg];
@@ -612,6 +633,7 @@
preloadPackageCacheKey = args[++curArg];
} else if (arg.equals("--preload-default")) {
preloadDefault = true;
+ expectRuntimeArgs = false;
} else if (arg.equals("--start-child-zygote")) {
startChildZygote = true;
} else if (arg.equals("--set-api-blacklist-exemptions")) {
@@ -619,6 +641,16 @@
// with the regular fork command.
apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
curArg = args.length;
+ expectRuntimeArgs = false;
+ } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
+ String rateStr = arg.substring(arg.indexOf('=') + 1);
+ try {
+ hiddenApiAccessLogSampleRate = Integer.parseInt(rateStr);
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(
+ "Invalid log sampling rate: " + rateStr, nfe);
+ }
+ expectRuntimeArgs = false;
} else {
break;
}
@@ -633,7 +665,7 @@
throw new IllegalArgumentException(
"Unexpected arguments after --preload-package.");
}
- } else if (!preloadDefault && apiBlacklistExemptions == null) {
+ } else if (expectRuntimeArgs) {
if (!seenRuntimeArgs) {
throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index c5d41db..6f58365 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -26,8 +26,8 @@
import android.icu.util.ULocale;
import android.opengl.EGL14;
import android.os.Build;
-import android.os.IInstalld;
import android.os.Environment;
+import android.os.IInstalld;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -44,16 +44,16 @@
import android.system.StructCapUserData;
import android.system.StructCapUserHeader;
import android.text.Hyphenator;
-import android.util.TimingsTraceLog;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.TimingsTraceLog;
import android.webkit.WebViewFactory;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
-
import com.android.internal.util.Preconditions;
+
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
import dalvik.system.ZygoteHooks;
@@ -67,8 +67,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.security.Security;
import java.security.Provider;
+import java.security.Security;
/**
* Startup class for the zygote process.
@@ -518,6 +518,10 @@
VMRuntime.getRuntime().setHiddenApiExemptions(exemptions);
}
+ public static void setHiddenApiAccessLogSampleRate(int percent) {
+ VMRuntime.getRuntime().setHiddenApiAccessLogSamplingRate(percent);
+ }
+
/**
* Creates a PathClassLoader for the given class path that is associated with a shared
* namespace, i.e., this classloader can access platform-private native libraries. The
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 24f2fbf..2b7221a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -64,6 +64,8 @@
in NotificationVisibility[] noLongerVisibleKeys);
void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
void onNotificationDirectReplied(String key);
+ void onNotificationSmartRepliesAdded(in String key, in int replyCount);
+ void onNotificationSmartReplySent(in String key, in int replyIndex);
void onNotificationSettingsViewed(String key);
void setSystemUiVisibility(int vis, int mask, String cause);
diff --git a/core/java/com/android/internal/util/StatLogger.java b/core/java/com/android/internal/util/StatLogger.java
new file mode 100644
index 0000000..1dac136
--- /dev/null
+++ b/core/java/com/android/internal/util/StatLogger.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.StatLoggerProto;
+import com.android.server.StatLoggerProto.Event;
+
+import java.io.PrintWriter;
+
+/**
+ * Simple class to keep track of the number of times certain events happened and their durations for
+ * benchmarking.
+ *
+ * @hide
+ */
+public class StatLogger {
+ private static final String TAG = "StatLogger";
+
+ private final Object mLock = new Object();
+
+ private final int SIZE;
+
+ @GuardedBy("mLock")
+ private final int[] mCountStats;
+
+ @GuardedBy("mLock")
+ private final long[] mDurationStats;
+
+ @GuardedBy("mLock")
+ private final int[] mCallsPerSecond;
+
+ @GuardedBy("mLock")
+ private final long[] mDurationPerSecond;
+
+ @GuardedBy("mLock")
+ private final int[] mMaxCallsPerSecond;
+
+ @GuardedBy("mLock")
+ private final long[] mMaxDurationPerSecond;
+
+ @GuardedBy("mLock")
+ private final long[] mMaxDurationStats;
+
+ @GuardedBy("mLock")
+ private long mNextTickTime = SystemClock.elapsedRealtime() + 1000;
+
+ private final String[] mLabels;
+
+ public StatLogger(String[] eventLabels) {
+ SIZE = eventLabels.length;
+ mCountStats = new int[SIZE];
+ mDurationStats = new long[SIZE];
+ mCallsPerSecond = new int[SIZE];
+ mMaxCallsPerSecond = new int[SIZE];
+ mDurationPerSecond = new long[SIZE];
+ mMaxDurationPerSecond = new long[SIZE];
+ mMaxDurationStats = new long[SIZE];
+ mLabels = eventLabels;
+ }
+
+ /**
+ * Return the current time in the internal time unit.
+ * Call it before an event happens, and
+ * give it back to the {@link #logDurationStat(int, long)}} after the event.
+ */
+ public long getTime() {
+ return SystemClock.elapsedRealtimeNanos() / 1000;
+ }
+
+ /**
+ * @see {@link #getTime()}
+ *
+ * @return the duration in microseconds.
+ */
+ public long logDurationStat(int eventId, long start) {
+ synchronized (mLock) {
+ final long duration = getTime() - start;
+ if (eventId >= 0 && eventId < SIZE) {
+ mCountStats[eventId]++;
+ mDurationStats[eventId] += duration;
+ } else {
+ Slog.wtf(TAG, "Invalid event ID: " + eventId);
+ return duration;
+ }
+ if (mMaxDurationStats[eventId] < duration) {
+ mMaxDurationStats[eventId] = duration;
+ }
+
+ // Keep track of the per-second max.
+ final long nowRealtime = SystemClock.elapsedRealtime();
+ if (nowRealtime > mNextTickTime) {
+ if (mMaxCallsPerSecond[eventId] < mCallsPerSecond[eventId]) {
+ mMaxCallsPerSecond[eventId] = mCallsPerSecond[eventId];
+ }
+ if (mMaxDurationPerSecond[eventId] < mDurationPerSecond[eventId]) {
+ mMaxDurationPerSecond[eventId] = mDurationPerSecond[eventId];
+ }
+
+ mCallsPerSecond[eventId] = 0;
+ mDurationPerSecond[eventId] = 0;
+
+ mNextTickTime = nowRealtime + 1000;
+ }
+
+ mCallsPerSecond[eventId]++;
+ mDurationPerSecond[eventId] += duration;
+
+ return duration;
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ dump(new IndentingPrintWriter(pw, " ").setIndent(prefix));
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.println("Stats:");
+ pw.increaseIndent();
+ for (int i = 0; i < SIZE; i++) {
+ final int count = mCountStats[i];
+ final double durationMs = mDurationStats[i] / 1000.0;
+
+ pw.println(String.format(
+ "%s: count=%d, total=%.1fms, avg=%.3fms, max calls/s=%d max dur/s=%.1fms"
+ + " max time=%.1fms",
+ mLabels[i], count, durationMs,
+ (count == 0 ? 0 : durationMs / count),
+ mMaxCallsPerSecond[i], mMaxDurationPerSecond[i] / 1000.0,
+ mMaxDurationStats[i] / 1000.0));
+ }
+ pw.decreaseIndent();
+ }
+ }
+
+ public void dumpProto(ProtoOutputStream proto, long fieldId) {
+ synchronized (mLock) {
+ final long outer = proto.start(fieldId);
+
+ for (int i = 0; i < mLabels.length; i++) {
+ final long inner = proto.start(StatLoggerProto.EVENTS);
+
+ proto.write(Event.EVENT_ID, i);
+ proto.write(Event.LABEL, mLabels[i]);
+ proto.write(Event.COUNT, mCountStats[i]);
+ proto.write(Event.TOTAL_DURATION_MICROS, mDurationStats[i]);
+
+ proto.end(inner);
+ }
+
+ proto.end(outer);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 35aae15..2ce5a0b 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1706,6 +1706,7 @@
contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
contentContainer.setTag(FLOATING_TOOLBAR_TAG);
+ contentContainer.setClipToOutline(true);
return contentContainer;
}
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 8e98ac9..b06b143 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -345,6 +345,7 @@
OVERCOUNTED = 10;
CAMERA = 11;
MEMORY = 12;
+ AMBIENT_DISPLAY = 13;
};
optional Sipper name = 1;
// UID, only valid for the USER sipper.
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index ab15d4f..503bd21 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -228,7 +228,7 @@
];
optional android.service.print.PrintServiceDumpProto print = 3010 [
- (section).type = SECTION_NONE, // Turn off until we get approval for it.
+ (section).type = SECTION_DUMPSYS,
(section).args = "print --proto"
];
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index a8d0825..ee371c1 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -220,6 +220,7 @@
optional float adjust_ime_amount = 10;
optional float adjust_divider_amount = 11;
optional .android.graphics.RectProto adjusted_bounds = 12;
+ optional bool animating_bounds = 13;
}
/* represents Task */
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index 8240d8a..c363710 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -88,7 +88,7 @@
option (android.msg_privacy).dest = DEST_AUTOMATIC;
optional bool connected_to_adb = 1;
- optional string last_key_recevied = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
+ optional string last_key_received = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
optional string user_keys = 3 [ (android.privacy).dest = DEST_LOCAL ];
optional string system_keys = 4 [ (android.privacy).dest = DEST_LOCAL ];
}
@@ -342,4 +342,4 @@
optional string manufacturer = 1;
optional string model = 2;
optional string version = 3;
-}
\ No newline at end of file
+}
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 3196d00..b7395cd 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -138,7 +138,6 @@
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_camera"
- android:tint="@color/notification_secondary_text_color_light"
android:background="?android:selectableItemBackgroundBorderless"
android:visibility="gone"
/>
@@ -147,7 +146,6 @@
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_mic"
- android:tint="@color/notification_secondary_text_color_light"
android:background="?android:selectableItemBackgroundBorderless"
android:layout_marginStart="4dp"
android:visibility="gone"
@@ -157,7 +155,6 @@
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_alert_window_layer"
- android:tint="@color/notification_secondary_text_color_light"
android:background="?android:selectableItemBackgroundBorderless"
android:layout_marginStart="4dp"
android:visibility="gone"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index bd824de..04f4d6e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4704,11 +4704,13 @@
<string name="locale_search_menu">Search</string>
<!-- Title of the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=30] -->
- <string name="app_suspended_title">Action not allowed</string>
+ <string name="app_suspended_title">Can\u2019t open app</string>
<!-- Default message shown in the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=NONE] -->
- <string name="app_suspended_default_message">The application <xliff:g id="app_name" example="GMail">%1$s</xliff:g> is currently disabled.</string>
+ <string name="app_suspended_default_message">
+ The app <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> isn\u2019t available right now. This is managed by <xliff:g id="app_name" example="Settings">%2$s</xliff:g>.
+ </string>
<!-- Title of the button to show users more details about why the app has been suspended [CHAR LIMIT=50]-->
- <string name="app_suspended_more_details">More details</string>
+ <string name="app_suspended_more_details">Learn more</string>
<!-- Title of a dialog. The string is asking if the user wants to turn on their work profile, which contains work apps that are managed by their employer. "Work" is an adjective. [CHAR LIMIT=30] -->
<string name="work_mode_off_title">Turn on work profile?</string>
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index f1b61a7..dce8a65 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,7 +18,7 @@
-->
<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
-<zen version="6">
+<zen version="7">
<allow alarms="true" media="true" system="false" calls="false" messages="false" reminders="false"
events="false" />
<!-- all visual effects that exist as of P -->
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index a5941b2..558e576 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -246,6 +246,7 @@
Settings.Global.HDMI_CONTROL_ENABLED,
Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE,
Settings.Global.HIDDEN_API_POLICY_P_APPS,
Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS,
Settings.Global.HIDE_ERROR_DIALOGS,
diff --git a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
index c205f96..6c05601 100644
--- a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
+++ b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
@@ -54,7 +54,7 @@
first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
// Do not copy NoCopySpan if specified so.
- final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */);
+ final SpannedString copied = new SpannedString(first, true /* ignoreNoCopySpan */);
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
assertNotNull(spans);
assertEquals(2, spans.length);
@@ -87,7 +87,7 @@
// Do not copy NoCopySpan if specified so.
final SpannedString copied = new SpannedString(
- new CustomSpannable(first), false /* copyNoCopySpan */);
+ new CustomSpannable(first), true /* ignoreNoCopySpan */);
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
assertNotNull(spans);
assertEquals(2, spans.length);
diff --git a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
index 0680924..380e315 100644
--- a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
+++ b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
@@ -54,7 +54,7 @@
first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
// Do not copy NoCopySpan if specified so.
- final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */);
+ final SpannedString copied = new SpannedString(first, true /* ignoreNoCopySpan */);
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
assertNotNull(spans);
assertEquals(2, spans.length);
@@ -87,7 +87,7 @@
// Do not copy NoCopySpan if specified so.
final SpannedString copied = new SpannedString(
- new CustomSpanned(first), false /* copyNoCopySpan */);
+ new CustomSpanned(first), true /* ignoreNoCopySpan */);
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
assertNotNull(spans);
assertEquals(2, spans.length);
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index 6e9401d..6ee74cb 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -208,6 +208,12 @@
}
@Test
+ public void fromSpec_wontCacheIfScreenHeightChanges() {
+ DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 4000, 1f);
+ assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f), not(sameInstance(cached)));
+ }
+
+ @Test
public void fromSpec_wontCacheIfDensityChanges() {
DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f);
assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f), not(sameInstance(cached)));
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 22867df..72d9bce 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -111,6 +111,8 @@
<family lang="und-Ethi">
<font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
<font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.otf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-Bold.otf</font>
</family>
<family lang="und-Hebr">
<font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
@@ -167,6 +169,8 @@
<family lang="und-Guru" variant="elegant">
<font weight="400" style="normal">NotoSansGurmukhi-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGurmukhi-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.otf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Bold.otf</font>
</family>
<family lang="und-Guru" variant="compact">
<font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
@@ -231,9 +235,15 @@
<font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
</family>
- <family lang="und-Sinh">
+ <family lang="und-Sinh" variant="elegant">
<font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
<font weight="700" style="normal">NotoSansSinhala-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.otf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-Bold.otf</font>
+ </family>
+ <family lang="und-Sinh" variant="compact">
+ <font weight="400" style="normal">NotoSansSinhalaUI-Regular.otf</font>
+ <font weight="700" style="normal">NotoSansSinhalaUI-Bold.otf</font>
</family>
<family lang="und-Khmr" variant="elegant">
<font weight="100" style="normal">NotoSansKhmer-VF.ttf
@@ -272,7 +282,9 @@
<axis tag="wdth" stylevalue="100.0" />
<axis tag="wght" stylevalue="190.0" />
</font>
- </family>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
+ </family>
<family lang="und-Khmr" variant="compact">
<font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
@@ -290,6 +302,8 @@
<family lang="und-Mymr" variant="elegant">
<font weight="400" style="normal">NotoSansMyanmar-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMyanmar-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
</family>
<family lang="und-Mymr" variant="compact">
<font weight="400" style="normal">NotoSansMyanmarUI-Regular.ttf</font>
@@ -303,6 +317,9 @@
<font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
<font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
</family>
+ <family lang="und-Ahom">
+ <font weight="400" style="normal">NotoSansAhom-Regular.otf</font>
+ </family>
<family lang="und-Adlm">
<font weight="400" style="normal">NotoSansAdlam-Regular.ttf</font>
</family>
@@ -354,6 +371,9 @@
<family lang="und-Egyp">
<font weight="400" style="normal">NotoSansEgyptianHieroglyphs-Regular.ttf</font>
</family>
+ <family lang="und-Elba">
+ <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font>
+ </family>
<family lang="und-Glag">
<font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
</family>
@@ -538,4 +558,64 @@
<family lang="und-Phag">
<font weight="400" style="normal">NotoSansPhagsPa-Regular.ttf</font>
</family>
+ <family lang="und-Hluw">
+ <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font>
+ </family>
+ <family lang="und-Bass">
+ <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font>
+ </family>
+ <family lang="und-Bhks">
+ <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font>
+ </family>
+ <family lang="und-Hatr">
+ <font weight="400" style="normal">NotoSansHatran-Regular.otf</font>
+ </family>
+ <family lang="und-Lina">
+ <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font>
+ </family>
+ <family lang="und-Mani">
+ <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font>
+ </family>
+ <family lang="und-Marc">
+ <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font>
+ </family>
+ <family lang="und-Merc">
+ <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font>
+ </family>
+ <family lang="und-Plrd">
+ <font weight="400" style="normal">NotoSansMiao-Regular.otf</font>
+ </family>
+ <family lang="und-Mroo">
+ <font weight="400" style="normal">NotoSansMro-Regular.otf</font>
+ </family>
+ <family lang="und-Mult">
+ <font weight="400" style="normal">NotoSansMultani-Regular.otf</font>
+ </family>
+ <family lang="und-Nbat">
+ <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font>
+ </family>
+ <family lang="und-Newa">
+ <font weight="400" style="normal">NotoSansNewa-Regular.otf</font>
+ </family>
+ <family lang="und-Narb">
+ <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font>
+ </family>
+ <family lang="und-Perm">
+ <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font>
+ </family>
+ <family lang="und-Hmng">
+ <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font>
+ </family>
+ <family lang="und-Palm">
+ <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font>
+ </family>
+ <family lang="und-Pauc">
+ <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font>
+ </family>
+ <family lang="und-Shrd">
+ <font weight="400" style="normal">NotoSansSharada-Regular.otf</font>
+ </family>
+ <family lang="und-Sora">
+ <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font>
+ </family>
</familyset>
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
index 88e768c..30389a29d 100644
--- a/keystore/java/android/security/KeyStoreException.java
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -16,12 +16,15 @@
package android.security;
+import android.annotation.TestApi;
+
/**
* KeyStore/keymaster exception with positive error codes coming from the KeyStore and negative
* ones from keymaster.
*
* @hide
*/
+@TestApi
public class KeyStoreException extends Exception {
private final int mErrorCode;
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index c0d0fb0..b2e0f67 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -19,6 +19,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.app.KeyguardManager;
import android.hardware.fingerprint.FingerprintManager;
import android.security.GateKeeper;
@@ -594,6 +595,14 @@
/**
* Returns {@code true} if the key is authorized to be used only if a test of user presence has
* been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
+ * It requires that the KeyStore implementation have a direct way to validate the user presence
+ * for example a KeyStore hardware backed strongbox can use a button press that is observable
+ * in hardware. A test for user presence is tangential to authentication. The test can be part
+ * of an authentication step as long as this step can be validated by the hardware protecting
+ * the key and cannot be spoofed. For example, a physical button press can be used as a test of
+ * user presence if the other pins connected to the button are not able to simulate a button
+ * press. There must be no way for the primary processor to fake a button press, or that
+ * button must not be used as a test of user presence.
*/
public boolean isUserPresenceRequired() {
return mUserPresenceRequired;
@@ -673,8 +682,8 @@
}
/**
- * Returns {@code true} if the screen must be unlocked for this key to be used for encryption or
- * signing. Decryption and signature verification will still be available when the screen is
+ * Returns {@code true} if the screen must be unlocked for this key to be used for decryption or
+ * signing. Encryption and signature verification will still be available when the screen is
* locked.
*
* @see Builder#setUnlockedDeviceRequired(boolean)
@@ -1180,6 +1189,14 @@
/**
* Sets whether a test of user presence is required to be performed between the
* {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+ * It requires that the KeyStore implementation have a direct way to validate the user
+ * presence for example a KeyStore hardware backed strongbox can use a button press that
+ * is observable in hardware. A test for user presence is tangential to authentication. The
+ * test can be part of an authentication step as long as this step can be validated by the
+ * hardware protecting the key and cannot be spoofed. For example, a physical button press
+ * can be used as a test of user presence if the other pins connected to the button are not
+ * able to simulate a button press.There must be no way for the primary processor to fake a
+ * button press, or that button must not be used as a test of user presence.
*/
@NonNull
public Builder setUserPresenceRequired(boolean required) {
@@ -1227,6 +1244,7 @@
*
* Sets whether to include a temporary unique ID field in the attestation certificate.
*/
+ @TestApi
@NonNull
public Builder setUniqueIdIncluded(boolean uniqueIdIncluded) {
mUniqueIdIncluded = uniqueIdIncluded;
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 41dc201..fdcad85 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -19,6 +19,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.app.KeyguardManager;
import android.hardware.fingerprint.FingerprintManager;
import android.security.GateKeeper;
@@ -447,7 +448,12 @@
* been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
* It requires that the KeyStore implementation have a direct way to validate the user presence
* for example a KeyStore hardware backed strongbox can use a button press that is observable
- * in hardware.
+ * in hardware. A test for user presence is tangential to authentication. The test can be part
+ * of an authentication step as long as this step can be validated by the hardware protecting
+ * the key and cannot be spoofed. For example, a physical button press can be used as a test of
+ * user presence if the other pins connected to the button are not able to simulate a button
+ * press. There must be no way for the primary processor to fake a button press, or that
+ * button must not be used as a test of user presence.
*/
public boolean isUserPresenceRequired() {
return mUserPresenceRequred;
@@ -496,6 +502,7 @@
* @see KeymasterUtils#addUserAuthArgs
* @hide
*/
+ @TestApi
public long getBoundToSpecificSecureUserId() {
return mBoundToSecureUserId;
}
@@ -511,8 +518,8 @@
}
/**
- * Returns {@code true} if the screen must be unlocked for this key to be used for encryption or
- * signing. Decryption and signature verification will still be available when the screen is
+ * Returns {@code true} if the screen must be unlocked for this key to be used for decryption or
+ * signing. Encryption and signature verification will still be available when the screen is
* locked.
*
* @see Builder#setUnlockedDeviceRequired(boolean)
@@ -843,7 +850,15 @@
/**
* Sets whether a test of user presence is required to be performed between the
- * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+ * {@code Signature.initSign()} and {@code Signature.sign()} method calls. It requires that
+ * the KeyStore implementation have a direct way to validate the user presence for example
+ * a KeyStore hardware backed strongbox can use a button press that is observable in
+ * hardware. A test for user presence is tangential to authentication. The test can be part
+ * of an authentication step as long as this step can be validated by the hardware
+ * protecting the key and cannot be spoofed. For example, a physical button press can be
+ * used as a test of user presence if the other pins connected to the button are not able
+ * to simulate a button press. There must be no way for the primary processor to fake a
+ * button press, or that button must not be used as a test of user presence.
*/
@NonNull
public Builder setUserPresenceRequired(boolean required) {
@@ -913,6 +928,7 @@
* @see KeyProtection#getBoundToSpecificSecureUserId()
* @hide
*/
+ @TestApi
public Builder setBoundToSpecificSecureUserId(long secureUserId) {
mBoundToSecureUserId = secureUserId;
return this;
diff --git a/libs/hwui/debug/gles_decls.in b/libs/hwui/debug/gles_decls.in
index 16574a7..3900959 100644
--- a/libs/hwui/debug/gles_decls.in
+++ b/libs/hwui/debug/gles_decls.in
@@ -387,7 +387,6 @@
GL_ENTRY(void, glDrawElementsBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex)
GL_ENTRY(void, glDrawRangeElementsBaseVertexOES, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex)
GL_ENTRY(void, glDrawElementsInstancedBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex)
-GL_ENTRY(void, glMultiDrawElementsBaseVertexOES, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex)
GL_ENTRY(void, glFramebufferTextureOES, GLenum target, GLenum attachment, GLuint texture, GLint level)
GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary)
GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const void *binary, GLint length)
@@ -541,4 +540,4 @@
GL_ENTRY(void, glTextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width)
GL_ENTRY(void, glTextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
GL_ENTRY(void, glTextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
-GL_ENTRY(void, glTextureViewEXT, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers)
\ No newline at end of file
+GL_ENTRY(void, glTextureViewEXT, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers)
diff --git a/libs/hwui/debug/gles_stubs.in b/libs/hwui/debug/gles_stubs.in
index 4064a39..7cba0c1 100644
--- a/libs/hwui/debug/gles_stubs.in
+++ b/libs/hwui/debug/gles_stubs.in
@@ -1165,9 +1165,6 @@
void API_ENTRY(glDrawElementsInstancedBaseVertexOES)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) {
CALL_GL_API(glDrawElementsInstancedBaseVertexOES, mode, count, type, indices, instancecount, basevertex);
}
-void API_ENTRY(glMultiDrawElementsBaseVertexOES)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) {
- CALL_GL_API(glMultiDrawElementsBaseVertexOES, mode, count, type, indices, primcount, basevertex);
-}
void API_ENTRY(glFramebufferTextureOES)(GLenum target, GLenum attachment, GLuint texture, GLint level) {
CALL_GL_API(glFramebufferTextureOES, target, attachment, texture, level);
}
@@ -1629,4 +1626,4 @@
}
void API_ENTRY(glTextureViewEXT)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) {
CALL_GL_API(glTextureViewEXT, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers);
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index aeef215..fdb7499 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -63,6 +63,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -4786,6 +4787,21 @@
}
/**
+ * Add {@link MicrophoneInfo} by device information while filtering certain types.
+ */
+ private void addMicrophonesFromAudioDeviceInfo(ArrayList<MicrophoneInfo> microphones,
+ HashSet<Integer> filterTypes) {
+ AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
+ for (AudioDeviceInfo device : devices) {
+ if (filterTypes.contains(device.getType())) {
+ continue;
+ }
+ MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
+ microphones.add(microphone);
+ }
+ }
+
+ /**
* Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics
* of all available microphones. The list is empty when no microphones are available
* on the device. An error during the query will result in an IOException being thrown.
@@ -4796,21 +4812,17 @@
public List<MicrophoneInfo> getMicrophones() throws IOException {
ArrayList<MicrophoneInfo> microphones = new ArrayList<MicrophoneInfo>();
int status = AudioSystem.getMicrophones(microphones);
+ HashSet<Integer> filterTypes = new HashSet<>();
+ filterTypes.add(AudioDeviceInfo.TYPE_TELEPHONY);
if (status != AudioManager.SUCCESS) {
- // fail and bail!
+ // fail and populate microphones with unknown characteristics by device information.
Log.e(TAG, "getMicrophones failed:" + status);
- return new ArrayList<MicrophoneInfo>(); // Always return a list.
+ addMicrophonesFromAudioDeviceInfo(microphones, filterTypes);
+ return microphones;
}
setPortIdForMicrophones(microphones);
- AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
- for (AudioDeviceInfo device : devices) {
- if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_MIC ||
- device.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
- continue;
- }
- MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
- microphones.add(microphone);
- }
+ filterTypes.add(AudioDeviceInfo.TYPE_BUILTIN_MIC);
+ addMicrophonesFromAudioDeviceInfo(microphones, filterTypes);
return microphones;
}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 4f0dccb..6b35dd4 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1628,7 +1628,6 @@
int status = native_get_active_microphones(activeMicrophones);
if (status != AudioManager.SUCCESS) {
Log.e(TAG, "getActiveMicrophones failed:" + status);
- return new ArrayList<MicrophoneInfo>();
}
AudioManager.setPortIdForMicrophones(activeMicrophones);
diff --git a/media/java/android/media/BufferingParams.java b/media/java/android/media/BufferingParams.java
index 521e897..aaae5e7b 100644
--- a/media/java/android/media/BufferingParams.java
+++ b/media/java/android/media/BufferingParams.java
@@ -17,6 +17,7 @@
package android.media;
import android.annotation.IntDef;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -63,6 +64,7 @@
* <p>Users should use {@link Builder} to change {@link BufferingParams}.
* @hide
*/
+@TestApi
public final class BufferingParams implements Parcelable {
private static final int BUFFERING_NO_MARK = -1;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index f9a1f8b..392a1eb 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.app.ActivityThread;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -1680,6 +1681,7 @@
* @hide
*/
@NonNull
+ @TestApi
public native BufferingParams getBufferingParams();
/**
@@ -1696,6 +1698,7 @@
* @throws IllegalArgumentException if params is invalid or not supported.
* @hide
*/
+ @TestApi
public native void setBufferingParams(@NonNull BufferingParams params);
/**
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 90b6bff..82d64f3 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1434,7 +1434,6 @@
int status = native_getActiveMicrophones(activeMicrophones);
if (status != AudioManager.SUCCESS) {
Log.e(TAG, "getActiveMicrophones failed:" + status);
- return new ArrayList<MicrophoneInfo>();
}
AudioManager.setPortIdForMicrophones(activeMicrophones);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 4a6df50..bab59f1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -29,5 +29,5 @@
void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
- void onProfileAudioStateChanged(int bluetoothProfile, int state);
+ void onAudioModeChanged();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index b74b2cd..06fe4de 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -27,6 +27,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.settingslib.R;
@@ -119,6 +120,12 @@
addHandler(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
new ActiveDeviceChangedHandler());
+ // Headset state changed broadcasts
+ addHandler(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
+ new AudioModeChangedHandler());
+ addHandler(TelephonyManager.ACTION_PHONE_STATE_CHANGED,
+ new AudioModeChangedHandler());
+
mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
}
@@ -456,4 +463,25 @@
}
}
}
+
+ private class AudioModeChangedHandler implements Handler {
+
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ final String action = intent.getAction();
+ if (action == null) {
+ Log.w(TAG, "AudioModeChangedHandler() action is null");
+ return;
+ }
+ dispatchAudioModeChanged();
+ }
+ }
+
+ private void dispatchAudioModeChanged() {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onAudioModeChanged();
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
index c4ca339..113256f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
@@ -41,6 +41,7 @@
private static final String TAG = "UserManagerHelper";
private final Context mContext;
private final UserManager mUserManager;
+ private final ActivityManager mActivityManager;
private OnUsersUpdateListener mUpdateListener;
private final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
@Override
@@ -52,6 +53,7 @@
public UserManagerHelper(Context context) {
mContext = context;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
/**
@@ -72,30 +74,64 @@
}
/**
- * Gets {@link UserInfo} for the current user.
+ * Gets UserInfo for the foreground user.
*
- * @return {@link UserInfo} for the current user.
+ * Concept of foreground user is relevant for the multi-user deployment. Foreground user
+ * corresponds to the currently "logged in" user.
+ *
+ * @return {@link UserInfo} for the foreground user.
*/
- public UserInfo getCurrentUserInfo() {
- return mUserManager.getUserInfo(UserHandle.myUserId());
+ public UserInfo getForegroundUserInfo() {
+ return mUserManager.getUserInfo(getForegroundUserId());
}
/**
- * Gets all the other users on the system that are not the current user.
- *
- * @return List of {@code UserInfo} for each user that is not the current user.
+ * @return Id of the foreground user.
*/
- public List<UserInfo> getAllUsersExcludesCurrentUser() {
- List<UserInfo> others = getAllUsers();
+ public int getForegroundUserId() {
+ return mActivityManager.getCurrentUser();
+ }
- for (Iterator<UserInfo> iterator = others.iterator(); iterator.hasNext(); ) {
- UserInfo userInfo = iterator.next();
- if (userInfo.id == UserHandle.myUserId()) {
- // Remove current user from the list.
- iterator.remove();
- }
- }
- return others;
+ /**
+ * Gets UserInfo for the user running the caller process.
+ *
+ * Differentiation between foreground user and current process user is relevant for multi-user
+ * deployments.
+ *
+ * Some multi-user aware components (like SystemUI) might run as a singleton - one component
+ * for all users. Current process user is then always the same for that component, even when
+ * the foreground user changes.
+ *
+ * @return {@link UserInfo} for the user running the current process.
+ */
+ public UserInfo getCurrentProcessUserInfo() {
+ return mUserManager.getUserInfo(getCurrentProcessUserId());
+ }
+
+ /**
+ * @return Id for the user running the current process.
+ */
+ public int getCurrentProcessUserId() {
+ return UserHandle.myUserId();
+ }
+
+ /**
+ * Gets all the other users on the system that are not the user running the current process.
+ *
+ * @return List of {@code UserInfo} for each user that is not the user running the process.
+ */
+ public List<UserInfo> getAllUsersExcludesCurrentProcessUser() {
+ return getAllUsersExceptUser(getCurrentProcessUserId());
+ }
+
+ /**
+ * Gets all the existing users on the system that are not the currently running as the
+ * foreground user.
+ *
+ * @return List of {@code UserInfo} for each user that is not the foreground user.
+ */
+ public List<UserInfo> getAllUsersExcludesForegroundUser() {
+ return getAllUsersExceptUser(getForegroundUserId());
}
/**
@@ -104,12 +140,22 @@
* @return List of {@code UserInfo} for each user that is not the system user.
*/
public List<UserInfo> getAllUsersExcludesSystemUser() {
+ return getAllUsersExceptUser(UserHandle.USER_SYSTEM);
+ }
+
+ /**
+ * Get all the users except the one with userId passed in.
+ *
+ * @param userId of the user not to be returned.
+ * @return All users other than user with userId.
+ */
+ public List<UserInfo> getAllUsersExceptUser(int userId) {
List<UserInfo> others = getAllUsers();
for (Iterator<UserInfo> iterator = others.iterator(); iterator.hasNext(); ) {
UserInfo userInfo = iterator.next();
- if (userIsSystemUser(userInfo)) {
- // Remove system user from the list.
+ if (userInfo.id == userId) {
+ // Remove user with userId from the list.
iterator.remove();
}
}
@@ -146,78 +192,115 @@
}
/**
- * Checks whether passed in user is the user that's currently logged in.
+ * Checks whether passed in user is the foreground user.
*
* @param userInfo User to check.
- * @return {@code true} if current user, {@code false} otherwise.
+ * @return {@code true} if foreground user, {@code false} otherwise.
*/
- public boolean userIsCurrentUser(UserInfo userInfo) {
- return getCurrentUserInfo().id == userInfo.id;
+ public boolean userIsForegroundUser(UserInfo userInfo) {
+ return getForegroundUserId() == userInfo.id;
}
- // Current user information accessors
+ /**
+ * Checks whether passed in user is the user that's running the current process.
+ *
+ * @param userInfo User to check.
+ * @return {@code true} if user running the process, {@code false} otherwise.
+ */
+ public boolean userIsRunningCurrentProcess(UserInfo userInfo) {
+ return getCurrentProcessUserId() == userInfo.id;
+ }
+
+ // Foreground user information accessors.
/**
- * Checks if the current user is a demo user.
+ * Checks if the foreground user is a guest user.
*/
- public boolean isDemoUser() {
+ public boolean foregroundUserIsGuestUser() {
+ return getForegroundUserInfo().isGuest();
+ }
+
+ /**
+ * Return whether the foreground user has a restriction.
+ *
+ * @param restriction Restriction to check. Should be a UserManager.* restriction.
+ * @return Whether that restriction exists for the foreground user.
+ */
+ public boolean foregroundUserHasUserRestriction(String restriction) {
+ return mUserManager.hasUserRestriction(restriction, getForegroundUserInfo().getUserHandle());
+ }
+
+ /**
+ * Checks if the foreground user can add new users.
+ */
+ public boolean foregroundUserCanAddUsers() {
+ return !foregroundUserHasUserRestriction(UserManager.DISALLOW_ADD_USER);
+ }
+
+ // Current process user information accessors
+
+ /**
+ * Checks if the calling app is running in a demo user.
+ */
+ public boolean currentProcessRunningAsDemoUser() {
return mUserManager.isDemoUser();
}
/**
- * Checks if the current user is a guest user.
+ * Checks if the calling app is running as a guest user.
*/
- public boolean isGuestUser() {
+ public boolean currentProcessRunningAsGuestUser() {
return mUserManager.isGuestUser();
}
/**
- * Checks if the current user is the system user (User 0).
+ * Checks whether this process is running under the system user.
*/
- public boolean isSystemUser() {
+ public boolean currentProcessRunningAsSystemUser() {
return mUserManager.isSystemUser();
}
- // Current user restriction accessors
+ // Current process user restriction accessors
/**
- * Return whether the current user has a restriction.
+ * Return whether the user running the current process has a restriction.
*
* @param restriction Restriction to check. Should be a UserManager.* restriction.
- * @return Whether that restriction exists for the current user.
+ * @return Whether that restriction exists for the user running the process.
*/
- public boolean hasUserRestriction(String restriction) {
+ public boolean currentProcessHasUserRestriction(String restriction) {
return mUserManager.hasUserRestriction(restriction);
}
/**
- * Checks if the current user can add new users.
+ * Checks if the user running the current process can add new users.
*/
- public boolean canAddUsers() {
- return !hasUserRestriction(UserManager.DISALLOW_ADD_USER);
+ public boolean currentProcessCanAddUsers() {
+ return !currentProcessHasUserRestriction(UserManager.DISALLOW_ADD_USER);
}
/**
- * Checks if the current user can remove users.
+ * Checks if the user running the current process can remove users.
*/
- public boolean canRemoveUsers() {
- return !hasUserRestriction(UserManager.DISALLOW_REMOVE_USER);
+ public boolean currentProcessCanRemoveUsers() {
+ return !currentProcessHasUserRestriction(UserManager.DISALLOW_REMOVE_USER);
}
/**
- * Checks if the current user is allowed to switch to another user.
+ * Checks if the user running the current process is allowed to switch to another user.
*/
- public boolean canSwitchUsers() {
- return !hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
+ public boolean currentProcessCanSwitchUsers() {
+ return !currentProcessHasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
}
/**
- * Checks if the current user can modify accounts. Demo and Guest users cannot modify accounts
- * even if the DISALLOW_MODIFY_ACCOUNTS restriction is not applied.
+ * Checks if the current process user can modify accounts. Demo and Guest users cannot modify
+ * accounts even if the DISALLOW_MODIFY_ACCOUNTS restriction is not applied.
*/
- public boolean canModifyAccounts() {
- return !hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS) && !isDemoUser()
- && !isGuestUser();
+ public boolean currentProcessCanModifyAccounts() {
+ return !currentProcessHasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)
+ && !currentProcessRunningAsDemoUser()
+ && !currentProcessRunningAsGuestUser();
}
// User actions
@@ -242,8 +325,8 @@
/**
* Tries to remove the user that's passed in. System user cannot be removed.
- * If the user to be removed is current user, it switches to the system user first, and then
- * removes the user.
+ * If the user to be removed is user currently running the process,
+ * it switches to the system user first, and then removes the user.
*
* @param userInfo User to be removed
* @return {@code true} if user is successfully removed, {@code false} otherwise.
@@ -254,7 +337,7 @@
return false;
}
- if (userInfo.id == getCurrentUserInfo().id) {
+ if (userInfo.id == getCurrentProcessUserId()) {
switchToUserId(UserHandle.USER_SYSTEM);
}
@@ -267,7 +350,7 @@
* @param userInfo User to switch to.
*/
public void switchToUser(UserInfo userInfo) {
- if (userInfo.id == getCurrentUserInfo().id) {
+ if (userInfo.id == getForegroundUserId()) {
return;
}
@@ -276,15 +359,6 @@
return;
}
- if (UserManager.isGuestUserEphemeral()) {
- // If switching from guest, we want to bring up the guest exit dialog instead of
- // switching
- UserInfo currUserInfo = getCurrentUserInfo();
- if (currUserInfo != null && currUserInfo.isGuest()) {
- return;
- }
- }
-
switchToUserId(userInfo.id);
}
@@ -348,6 +422,9 @@
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(Intent.ACTION_USER_STOPPED);
+ filter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(mUserChangeReceiver, UserHandle.ALL, filter, null, null);
}
@@ -366,9 +443,7 @@
private void switchToUserId(int id) {
try {
- final ActivityManager am = (ActivityManager)
- mContext.getSystemService(Context.ACTIVITY_SERVICE);
- am.switchUser(id);
+ mActivityManager.switchUser(id);
} catch (Exception e) {
Log.e(TAG, "Couldn't switch user.", e);
}
@@ -389,4 +464,3 @@
void onUsersUpdate();
}
}
-
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
index 3f1fcbb..15f7770 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
@@ -58,7 +58,7 @@
private UserManagerHelper.OnUsersUpdateListener mTestListener;
private UserManagerHelper mHelper;
- private UserInfo mCurrentUser;
+ private UserInfo mCurrentProcessUser;
private UserInfo mSystemUser;
@Before
@@ -70,13 +70,13 @@
.thenReturn(InstrumentationRegistry.getTargetContext().getResources());
mHelper = new UserManagerHelper(mContext);
- mCurrentUser = createUserInfoForId(UserHandle.myUserId());
+ mCurrentProcessUser = createUserInfoForId(UserHandle.myUserId());
mSystemUser = createUserInfoForId(UserHandle.USER_SYSTEM);
- when(mUserManager.getUserInfo(UserHandle.myUserId())).thenReturn(mCurrentUser);
+ when(mUserManager.getUserInfo(UserHandle.myUserId())).thenReturn(mCurrentProcessUser);
}
@Test
- public void testUserIsSystemUser() {
+ public void userIsSystemUser() {
UserInfo testInfo = new UserInfo();
testInfo.id = UserHandle.USER_SYSTEM;
@@ -87,33 +87,7 @@
}
@Test
- public void testGetAllUsersExcludesCurrentUser() {
- int currentUser = UserHandle.myUserId();
-
- UserInfo otherUser1 = createUserInfoForId(currentUser + 1);
- UserInfo otherUser2 = createUserInfoForId(currentUser - 1);
- UserInfo otherUser3 = createUserInfoForId(currentUser + 2);
-
- List<UserInfo> testUsers = new ArrayList<>();
- testUsers.add(otherUser1);
- testUsers.add(otherUser2);
- testUsers.add(mCurrentUser);
- testUsers.add(otherUser3);
-
- when(mUserManager.getUsers(true)).thenReturn(testUsers);
-
- // Should return 3 users that don't have currentUser id.
- assertThat(mHelper.getAllUsersExcludesCurrentUser().size()).isEqualTo(3);
- // Should not contain current user.
- assertThat(mHelper.getAllUsersExcludesCurrentUser()).doesNotContain(mCurrentUser);
- // Should contain non-current users.
- assertThat(mHelper.getAllUsersExcludesCurrentUser()).contains(otherUser1);
- assertThat(mHelper.getAllUsersExcludesCurrentUser()).contains(otherUser2);
- assertThat(mHelper.getAllUsersExcludesCurrentUser()).contains(otherUser3);
- }
-
- @Test
- public void testGetAllUsersExcludesSystemUser() {
+ public void getAllUsersExcludesSystemUser() {
UserInfo otherUser1 = createUserInfoForId(10);
UserInfo otherUser2 = createUserInfoForId(11);
UserInfo otherUser3 = createUserInfoForId(12);
@@ -127,17 +101,41 @@
when(mUserManager.getUsers(true)).thenReturn(testUsers);
// Should return 3 users that don't have SYSTEM USER id.
- assertThat(mHelper.getAllUsersExcludesSystemUser().size()).isEqualTo(3);
- // Should not contain system user.
- assertThat(mHelper.getAllUsersExcludesSystemUser()).doesNotContain(mSystemUser);
- // Should contain non-system users.
- assertThat(mHelper.getAllUsersExcludesSystemUser()).contains(otherUser1);
- assertThat(mHelper.getAllUsersExcludesSystemUser()).contains(otherUser2);
- assertThat(mHelper.getAllUsersExcludesSystemUser()).contains(otherUser3);
+ assertThat(mHelper.getAllUsersExcludesSystemUser()).hasSize(3);
+ assertThat(mHelper.getAllUsersExcludesSystemUser())
+ .containsExactly(otherUser1, otherUser2, otherUser3);
}
@Test
- public void testGetAllUsers() {
+ public void getAllUsersExceptUser() {
+ UserInfo user1 = createUserInfoForId(10);
+ UserInfo user2 = createUserInfoForId(10);
+ UserInfo user3 = createUserInfoForId(12);
+
+ List<UserInfo> testUsers = new ArrayList<>();
+ testUsers.add(user1);
+ testUsers.add(user2);
+ testUsers.add(user3);
+
+ when(mUserManager.getUsers(true)).thenReturn(new ArrayList<>(testUsers));
+
+ // Should return all 3 users.
+ assertThat(mHelper.getAllUsersExceptUser(9).size()).isEqualTo(3);
+
+ // Should return only user 12.
+ assertThat(mHelper.getAllUsersExceptUser(10).size()).isEqualTo(1);
+ assertThat(mHelper.getAllUsersExceptUser(10)).contains(user3);
+
+ when(mUserManager.getUsers(true)).thenReturn(new ArrayList<>(testUsers));
+
+ // Should drop user 12.
+ assertThat(mHelper.getAllUsersExceptUser(12).size()).isEqualTo(2);
+ assertThat(mHelper.getAllUsersExceptUser(12)).contains(user1);
+ assertThat(mHelper.getAllUsersExceptUser(12)).contains(user2);
+ }
+
+ @Test
+ public void getAllUsers() {
int currentUser = UserHandle.myUserId();
UserInfo otherUser1 = createUserInfoForId(currentUser + 1);
@@ -147,21 +145,18 @@
List<UserInfo> testUsers = new ArrayList<>();
testUsers.add(otherUser1);
testUsers.add(otherUser2);
- testUsers.add(mCurrentUser);
+ testUsers.add(mCurrentProcessUser);
testUsers.add(otherUser3);
when(mUserManager.getUsers(true)).thenReturn(testUsers);
- // Should return 3 users that don't have currentUser id.
assertThat(mHelper.getAllUsers().size()).isEqualTo(4);
- assertThat(mHelper.getAllUsers()).contains(mCurrentUser);
- assertThat(mHelper.getAllUsers()).contains(otherUser1);
- assertThat(mHelper.getAllUsers()).contains(otherUser2);
- assertThat(mHelper.getAllUsers()).contains(otherUser3);
+ assertThat(mHelper.getAllUsers())
+ .containsExactly(mCurrentProcessUser, otherUser1, otherUser2, otherUser3);
}
@Test
- public void testUserCanBeRemoved() {
+ public void userCanBeRemoved() {
UserInfo testInfo = new UserInfo();
// System user cannot be removed.
@@ -173,71 +168,59 @@
}
@Test
- public void testUserIsCurrentUser() {
- UserInfo testInfo = new UserInfo();
-
- // System user cannot be removed.
- testInfo.id = UserHandle.myUserId();
- assertThat(mHelper.userIsCurrentUser(testInfo)).isTrue();
-
- testInfo.id = UserHandle.myUserId() + 2;
- assertThat(mHelper.userIsCurrentUser(testInfo)).isFalse();
- }
-
- @Test
- public void testCanAddUsers() {
+ public void currentProcessCanAddUsers() {
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)).thenReturn(false);
- assertThat(mHelper.canAddUsers()).isTrue();
+ assertThat(mHelper.currentProcessCanAddUsers()).isTrue();
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)).thenReturn(true);
- assertThat(mHelper.canAddUsers()).isFalse();
+ assertThat(mHelper.currentProcessCanAddUsers()).isFalse();
}
@Test
- public void testCanRemoveUsers() {
+ public void currentProcessCanRemoveUsers() {
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)).thenReturn(false);
- assertThat(mHelper.canRemoveUsers()).isTrue();
+ assertThat(mHelper.currentProcessCanRemoveUsers()).isTrue();
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)).thenReturn(true);
- assertThat(mHelper.canRemoveUsers()).isFalse();
+ assertThat(mHelper.currentProcessCanRemoveUsers()).isFalse();
}
@Test
- public void testCanSwitchUsers() {
+ public void currentProcessCanSwitchUsers() {
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(false);
- assertThat(mHelper.canSwitchUsers()).isTrue();
+ assertThat(mHelper.currentProcessCanSwitchUsers()).isTrue();
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(true);
- assertThat(mHelper.canSwitchUsers()).isFalse();
+ assertThat(mHelper.currentProcessCanSwitchUsers()).isFalse();
}
@Test
- public void testGuestCannotModifyAccounts() {
- assertThat(mHelper.canModifyAccounts()).isTrue();
+ public void currentProcessRunningAsGuestCannotModifyAccounts() {
+ assertThat(mHelper.currentProcessCanModifyAccounts()).isTrue();
when(mUserManager.isGuestUser()).thenReturn(true);
- assertThat(mHelper.canModifyAccounts()).isFalse();
+ assertThat(mHelper.currentProcessCanModifyAccounts()).isFalse();
}
@Test
- public void testDemoUserCannotModifyAccounts() {
- assertThat(mHelper.canModifyAccounts()).isTrue();
+ public void currentProcessRunningAsDemoUserCannotModifyAccounts() {
+ assertThat(mHelper.currentProcessCanModifyAccounts()).isTrue();
when(mUserManager.isDemoUser()).thenReturn(true);
- assertThat(mHelper.canModifyAccounts()).isFalse();
+ assertThat(mHelper.currentProcessCanModifyAccounts()).isFalse();
}
@Test
- public void testUserWithDisallowModifyAccountsRestrictionCannotModifyAccounts() {
- assertThat(mHelper.canModifyAccounts()).isTrue();
+ public void currentProcessWithDisallowModifyAccountsRestrictionCannotModifyAccounts() {
+ assertThat(mHelper.currentProcessCanModifyAccounts()).isTrue();
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS))
.thenReturn(true);
- assertThat(mHelper.canModifyAccounts()).isFalse();
+ assertThat(mHelper.currentProcessCanModifyAccounts()).isFalse();
}
@Test
- public void testCreateNewUser() {
+ public void createNewUser() {
// Verify createUser on UserManager gets called.
mHelper.createNewUser("Test User");
verify(mUserManager).createUser("Test User", 0);
@@ -252,52 +235,36 @@
}
@Test
- public void testRemoveUser() {
+ public void removeUser() {
// Cannot remove system user.
assertThat(mHelper.removeUser(mSystemUser)).isFalse();
// Removing non-current, non-system user, simply calls removeUser.
- UserInfo userToRemove = createUserInfoForId(mCurrentUser.id + 2);
+ UserInfo userToRemove = createUserInfoForId(mCurrentProcessUser.id + 2);
+
mHelper.removeUser(userToRemove);
- verify(mUserManager).removeUser(mCurrentUser.id + 2);
+ verify(mUserManager).removeUser(mCurrentProcessUser.id + 2);
}
@Test
- public void testSwitchToUser() {
- // Switching to current user doesn't do anything.
- mHelper.switchToUser(mCurrentUser);
- verify(mActivityManager, never()).switchUser(mCurrentUser.id);
-
- // Switching to Guest calls createGuest.
- UserInfo guestInfo = new UserInfo(mCurrentUser.id + 1, "Test Guest", UserInfo.FLAG_GUEST);
- mHelper.switchToUser(guestInfo);
- verify(mUserManager).createGuest(mContext, "Test Guest");
-
- // Switching to non-current, non-guest user, simply calls switchUser.
- UserInfo userToSwitchTo = new UserInfo(mCurrentUser.id + 5, "Test User", 0);
- mHelper.switchToUser(userToSwitchTo);
- verify(mActivityManager).switchUser(mCurrentUser.id + 5);
- }
-
- @Test
- public void testSwitchToGuest() {
+ public void switchToGuest() {
mHelper.switchToGuest("Test Guest");
verify(mUserManager).createGuest(mContext, "Test Guest");
- UserInfo guestInfo = new UserInfo(mCurrentUser.id + 2, "Test Guest", UserInfo.FLAG_GUEST);
+ UserInfo guestInfo = new UserInfo(21, "Test Guest", UserInfo.FLAG_GUEST);
when(mUserManager.createGuest(mContext, "Test Guest")).thenReturn(guestInfo);
mHelper.switchToGuest("Test Guest");
- verify(mActivityManager).switchUser(mCurrentUser.id + 2);
+ verify(mActivityManager).switchUser(21);
}
@Test
- public void testGetUserIcon() {
- mHelper.getUserIcon(mCurrentUser);
- verify(mUserManager).getUserIcon(mCurrentUser.id);
+ public void getUserIcon() {
+ mHelper.getUserIcon(mCurrentProcessUser);
+ verify(mUserManager).getUserIcon(mCurrentProcessUser.id);
}
@Test
- public void testScaleUserIcon() {
+ public void scaleUserIcon() {
Bitmap fakeIcon = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Drawable scaledIcon = mHelper.scaleUserIcon(fakeIcon, 300);
assertThat(scaledIcon.getIntrinsicWidth()).isEqualTo(300);
@@ -305,14 +272,14 @@
}
@Test
- public void testSetUserName() {
- UserInfo testInfo = createUserInfoForId(mCurrentUser.id + 3);
+ public void setUserName() {
+ UserInfo testInfo = createUserInfoForId(mCurrentProcessUser.id + 3);
mHelper.setUserName(testInfo, "New Test Name");
- verify(mUserManager).setUserName(mCurrentUser.id + 3, "New Test Name");
+ verify(mUserManager).setUserName(mCurrentProcessUser.id + 3, "New Test Name");
}
@Test
- public void testRegisterUserChangeReceiver() {
+ public void registerUserChangeReceiver() {
mHelper.registerOnUsersUpdateListener(mTestListener);
ArgumentCaptor<BroadcastReceiver> receiverCaptor =
@@ -335,10 +302,14 @@
// Verify the presence of each intent in the filter.
// Verify the exact number of filters. Every time a new intent is added, this test should
// get updated.
- assertThat(filterCaptor.getValue().countActions()).isEqualTo(3);
+ assertThat(filterCaptor.getValue().countActions()).isEqualTo(6);
assertThat(filterCaptor.getValue().hasAction(Intent.ACTION_USER_REMOVED)).isTrue();
assertThat(filterCaptor.getValue().hasAction(Intent.ACTION_USER_ADDED)).isTrue();
assertThat(filterCaptor.getValue().hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue();
+ assertThat(filterCaptor.getValue().hasAction(Intent.ACTION_USER_SWITCHED)).isTrue();
+ assertThat(filterCaptor.getValue().hasAction(Intent.ACTION_USER_STOPPED)).isTrue();
+ assertThat(filterCaptor.getValue().hasAction(Intent.ACTION_USER_UNLOCKED)).isTrue();
+
// Verify that calling the receiver calls the listener.
receiverCaptor.getValue().onReceive(mContext, new Intent());
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
new file mode 100644
index 0000000..d1e37f6
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.bluetooth;
+
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothHeadset;
+import android.content.Context;
+import android.content.Intent;
+
+import android.telephony.TelephonyManager;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothEventManagerTest {
+
+ @Mock
+ private LocalBluetoothAdapter mLocalAdapter;
+ @Mock
+ private CachedBluetoothDeviceManager mCachedDeviceManager;
+ @Mock
+ private BluetoothCallback mBluetoothCallback;
+
+ private Context mContext;
+ private Intent mIntent;
+ private BluetoothEventManager mBluetoothEventManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+
+ mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter,
+ mCachedDeviceManager, mContext);
+ }
+
+ /**
+ * Intent ACTION_AUDIO_STATE_CHANGED should dispatch to callback.
+ */
+ @Test
+ public void intentWithExtraState_audioStateChangedShouldDispatchToRegisterCallback() {
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback).onAudioModeChanged();
+ }
+
+ /**
+ * Intent ACTION_PHONE_STATE_CHANGED should dispatch to callback.
+ */
+ @Test
+ public void intentWithExtraState_phoneStateChangedShouldDispatchToRegisterCallback() {
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback).onAudioModeChanged();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
new file mode 100644
index 0000000..4a8ef1e
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.testutils.shadow;
+
+import android.app.ActivityManager;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+import org.robolectric.shadow.api.Shadow;
+
+@Implements(ActivityManager.class)
+public class ShadowActivityManager {
+ private static int sCurrentUserId = 0;
+ private int mUserSwitchedTo = -1;
+
+ @Resetter
+ public void reset() {
+ sCurrentUserId = 0;
+ mUserSwitchedTo = 0;
+ }
+
+ @Implementation
+ public static int getCurrentUser() {
+ return sCurrentUserId;
+ }
+
+ @Implementation
+ public boolean switchUser(int userId) {
+ mUserSwitchedTo = userId;
+ return true;
+ }
+
+ public boolean getSwitchUserCalled() {
+ return mUserSwitchedTo != -1;
+ }
+
+ public int getUserSwitchedTo() {
+ return mUserSwitchedTo;
+ }
+
+ public static void setCurrentUser(int userId) {
+ sCurrentUserId = userId;
+ }
+
+ public static ShadowActivityManager getShadow() {
+ return (ShadowActivityManager) Shadow.extract(
+ RuntimeEnvironment.application.getSystemService(ActivityManager.class));
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
new file mode 100644
index 0000000..f2ea3a4
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settingslib.testutils.shadow.ShadowActivityManager;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = { ShadowActivityManager.class, UserManagerHelperRoboTest.ShadowUserHandle.class})
+public class UserManagerHelperRoboTest {
+ @Mock
+ private Context mContext;
+ @Mock
+ private UserManager mUserManager;
+
+ private UserManagerHelper mHelper;
+
+ @Before
+ public void setUpMocksAndUserManagerHelper() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(
+ RuntimeEnvironment.application.getSystemService(ActivityManager.class));
+ mHelper = new UserManagerHelper(mContext);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowActivityManager.getShadow().reset();
+ }
+
+ @Test
+ public void getForegroundUserId() {
+ ShadowActivityManager.setCurrentUser(15);
+ assertThat(mHelper.getForegroundUserId()).isEqualTo(15);
+ }
+
+ @Test
+ public void getForegroundUserInfo() {
+ ShadowActivityManager.setCurrentUser(17);
+ when(mUserManager.getUserInfo(ShadowActivityManager.getCurrentUser()))
+ .thenReturn(createUserInfoForId(ShadowActivityManager.getCurrentUser()));
+ assertThat(mHelper.getForegroundUserInfo().id).isEqualTo(17);
+ }
+
+ @Test
+ public void getCurrentProcessUserId() {
+ ShadowUserHandle.setUid(11);
+ assertThat(mHelper.getCurrentProcessUserId()).isEqualTo(11);
+ }
+
+ @Test
+ public void getCurrentProcessUserInfo() {
+ ShadowUserHandle.setUid(12);
+ when(mUserManager.getUserInfo(UserHandle.myUserId()))
+ .thenReturn(createUserInfoForId(UserHandle.myUserId()));
+ assertThat(mHelper.getCurrentProcessUserInfo().id).isEqualTo(12);
+ }
+
+ @Test
+ public void getAllUsersExcludesCurrentProcessUser() {
+ ShadowUserHandle.setUid(12);
+ UserInfo currentProcessUser = createUserInfoForId(12);
+
+ UserInfo otherUser1 = createUserInfoForId(13);
+ UserInfo otherUser2 = createUserInfoForId(11);
+ UserInfo otherUser3 = createUserInfoForId(14);
+
+ List<UserInfo> testUsers = new ArrayList<>();
+ testUsers.add(otherUser1);
+ testUsers.add(otherUser2);
+ testUsers.add(currentProcessUser);
+ testUsers.add(otherUser3);
+
+ when(mUserManager.getUsers(true)).thenReturn(testUsers);
+
+ // Should return 3 users that don't have currentProcessUser id.
+ assertThat(mHelper.getAllUsersExcludesCurrentProcessUser()).hasSize(3);
+ assertThat(mHelper.getAllUsersExcludesCurrentProcessUser())
+ .containsExactly(otherUser1, otherUser2, otherUser3);
+ }
+
+ @Test
+ public void getAllUsersExcludesForegroundUser() {
+ ShadowActivityManager.setCurrentUser(17);
+ UserInfo foregroundUser = createUserInfoForId(17);
+
+ UserInfo otherUser1 = createUserInfoForId(11);
+ UserInfo otherUser2 = createUserInfoForId(18);
+ UserInfo otherUser3 = createUserInfoForId(16);
+
+ List<UserInfo> testUsers = new ArrayList<>();
+ testUsers.add(otherUser1);
+ testUsers.add(otherUser2);
+ testUsers.add(foregroundUser);
+ testUsers.add(otherUser3);
+
+ when(mUserManager.getUsers(true)).thenReturn(testUsers);
+
+ // Should return 3 users that don't have foregroundUser id.
+ assertThat(mHelper.getAllUsersExcludesForegroundUser()).hasSize(3);
+ assertThat(mHelper.getAllUsersExcludesForegroundUser())
+ .containsExactly(otherUser1, otherUser2, otherUser3);
+ }
+
+ @Test
+ public void userIsForegroundUser() {
+ ShadowActivityManager.setCurrentUser(10);
+ assertThat(mHelper.userIsForegroundUser(createUserInfoForId(10))).isTrue();
+ assertThat(mHelper.userIsForegroundUser(createUserInfoForId(11))).isFalse();
+
+ ShadowActivityManager.setCurrentUser(11);
+ assertThat(mHelper.userIsForegroundUser(createUserInfoForId(11))).isTrue();
+ }
+
+ @Test
+ public void userIsRunningCurrentProcess() {
+ ShadowUserHandle.setUid(10);
+ assertThat(mHelper.userIsRunningCurrentProcess(createUserInfoForId(10))).isTrue();
+ assertThat(mHelper.userIsRunningCurrentProcess(createUserInfoForId(11))).isFalse();
+
+ ShadowUserHandle.setUid(11);
+ assertThat(mHelper.userIsRunningCurrentProcess(createUserInfoForId(11))).isTrue();
+ }
+
+ @Test
+ public void removingCurrentProcessUserSwitchesToSystemUser() {
+ // Set currentProcess user to be user 10.
+ ShadowUserHandle.setUid(10);
+
+ // Removing a currentProcess user, calls "switch" to system user
+ mHelper.removeUser(createUserInfoForId(10));
+ assertThat(ShadowActivityManager.getShadow().getSwitchUserCalled()).isTrue();
+ assertThat(ShadowActivityManager.getShadow().getUserSwitchedTo()).isEqualTo(0);
+
+ verify(mUserManager).removeUser(10);
+ }
+
+ @Test
+ public void switchToUser() {
+ ShadowActivityManager.setCurrentUser(20);
+
+ // Switching to foreground user doesn't do anything.
+ mHelper.switchToUser(createUserInfoForId(20));
+ assertThat(ShadowActivityManager.getShadow().getSwitchUserCalled()).isFalse();
+
+ // Switching to Guest calls createGuest.
+ UserInfo guestInfo = new UserInfo(21, "Test Guest", UserInfo.FLAG_GUEST);
+ mHelper.switchToUser(guestInfo);
+ verify(mUserManager).createGuest(mContext, "Test Guest");
+
+ // Switching to non-current, non-guest user, simply calls switchUser.
+ UserInfo userToSwitchTo = new UserInfo(22, "Test User", 0);
+ mHelper.switchToUser(userToSwitchTo);
+ assertThat(ShadowActivityManager.getShadow().getSwitchUserCalled()).isTrue();
+ assertThat(ShadowActivityManager.getShadow().getUserSwitchedTo()).isEqualTo(22);
+ }
+
+ private UserInfo createUserInfoForId(int id) {
+ UserInfo userInfo = new UserInfo();
+ userInfo.id = id;
+ return userInfo;
+ }
+
+ @Implements(UserHandle.class)
+ public static class ShadowUserHandle {
+ private static int sUid = 0; // SYSTEM by default
+
+ public static void setUid(int uid) {
+ sUid = uid;
+ }
+
+ @Implementation
+ public static int myUserId() {
+ return sUid;
+ }
+
+ @Resetter
+ public static void reset() {
+ sUid = 0;
+ }
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 313f73f..f728684 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -253,6 +253,7 @@
&& !RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS.contains(key)) {
Log.w(TAG, "Not restoring unrecognized key '"
+ key + "' from future version " + appVersionCode);
+ data.skipEntityData();
continue;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a6d6250..022e306 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2935,7 +2935,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 162;
+ private static final int SETTINGS_VERSION = 163;
private final int mUserId;
@@ -3709,6 +3709,21 @@
currentVersion = 162;
}
+ if (currentVersion == 162) {
+ // Version 162: Add a gesture for silencing phones
+ final SettingsState settings = getGlobalSettingsLocked();
+ final Setting currentSetting = settings.getSettingLocked(
+ Global.SHOW_ZEN_UPGRADE_NOTIFICATION);
+ if (!currentSetting.isNull()
+ && TextUtils.equals("0", currentSetting.getValue())) {
+ settings.insertSettingLocked(
+ Global.SHOW_ZEN_UPGRADE_NOTIFICATION, "1",
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+
+ currentVersion = 163;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml b/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded.xml
similarity index 97%
rename from packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml
rename to packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded.xml
index 85896e8..5add2a8 100644
--- a/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml
+++ b/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded.xml
@@ -15,8 +15,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="21dp"
- android:height="22dp"
+ android:width="32dp"
+ android:height="32dp"
android:viewportWidth="21"
android:viewportHeight="22">
diff --git a/packages/SimAppDialog/res/drawable/placeholder.xml b/packages/SimAppDialog/res/drawable/placeholder.xml
deleted file mode 100644
index 53eee74..0000000
--- a/packages/SimAppDialog/res/drawable/placeholder.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<!-- TODO(b/72511181): replace when illustration is finished -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="270dp"
- android:height="270dp"
- android:viewportHeight="270.0"
- android:viewportWidth="270.0">
- <path android:fillColor="#E8EAED"
- android:pathData="M183.54,265H84.88c-7.63,0 -13.81,-6.18 -13.81,-13.81V18.81C71.07,11.18 77.25,5 84.88,5h98.66c7.63,0 13.81,6.18 13.81,13.81v232.38C197.35,258.82 191.17,265 183.54,265z"/>
- <path android:fillColor="#BDC1C6"
- android:pathData="M183.54,6.63c6.72,0 12.18,5.46 12.18,12.18v232.38c0,6.72 -5.46,12.18 -12.18,12.18H84.88c-6.72,0 -12.18,-5.46 -12.18,-12.18V18.81c0,-6.72 5.46,-12.18 12.18,-12.18H183.54M183.54,5H84.88c-7.63,0 -13.81,6.18 -13.81,13.81v232.38c0,7.63 6.18,13.81 13.81,13.81h98.66c7.63,0 13.81,-6.18 13.81,-13.81V18.81C197.35,11.18 191.17,5 183.54,5L183.54,5z"/>
- <path android:fillColor="#FFFFFF"
- android:pathData="M186.34,243.74H82.08c-2.41,0 -4.36,-1.95 -4.36,-4.36V30.61c0,-2.41 1.95,-4.36 4.36,-4.36h104.26c2.41,0 4.36,1.95 4.36,4.36v208.78C190.7,241.79 188.75,243.74 186.34,243.74z"/>
- <path android:fillColor="#BDC1C6"
- android:pathData="M156.07,254.78h-43.72c-0.65,0 -1.18,-0.53 -1.18,-1.18v-0.08c0,-0.65 0.53,-1.18 1.18,-1.18h43.72c0.65,0 1.18,0.53 1.18,1.18v0.08C157.25,254.25 156.72,254.78 156.07,254.78z"/>
- <path android:fillColor="#BDC1C6"
- android:pathData="M156.07,17.67h-43.72c-0.65,0 -1.18,-0.53 -1.18,-1.18V16.4c0,-0.65 0.53,-1.18 1.18,-1.18l43.72,0c0.65,0 1.18,0.53 1.18,1.18v0.08C157.25,17.14 156.72,17.67 156.07,17.67z"/>
- <path android:fillColor="#BDC1C6"
- android:pathData="M197.85,84.16h-0.5V67.51h0.5c0.6,0 1.08,0.48 1.08,1.08v14.5C198.93,83.68 198.45,84.16 197.85,84.16z"/>
- <path android:fillColor="#BDC1C6"
- android:pathData="M197.41,136.45h-0.06v-32.87h0.06c0.84,0 1.52,0.68 1.52,1.52v29.84C198.93,135.77 198.25,136.45 197.41,136.45z"/>
- <path android:fillColor="#BDC1C6"
- android:pathData="M119.3,74.73l2.71,2.71c6.74,-6.74 17.67,-6.74 24.4,0l2.71,-2.71C140.89,66.49 127.54,66.49 119.3,74.73zM130.15,85.57l4.07,4.07l4.07,-4.07C136.04,83.33 132.39,83.33 130.15,85.57zM124.72,80.15l2.71,2.71c3.74,-3.74 9.82,-3.74 13.56,0l2.71,-2.71C138.46,74.91 129.96,74.91 124.72,80.15z"/>
- <path android:fillColor="#BDC1C6"
- android:pathData="M143.7,179h-1.36v-2.71h-2.71V179h-10.85v-2.71h-2.71V179h-1.36c-1.5,0 -2.7,1.21 -2.7,2.71l-0.01,18.98c0,1.5 1.21,2.71 2.71,2.71h18.98c1.5,0 2.71,-1.21 2.71,-2.71v-18.98C146.41,180.22 145.2,179 143.7,179zM143.7,200.7h-18.98v-14.91h18.98V200.7zM127.43,188.49h6.78v6.78h-6.78V188.49z"/>
- <path android:fillColor="#BDC1C6"
- android:pathData="M146.41,144.49v-18.98c0,-1.5 -1.21,-2.71 -2.71,-2.71h-18.98c-1.5,0 -2.71,1.21 -2.71,2.71v18.98c0,1.5 1.21,2.71 2.71,2.71h18.98C145.2,147.2 146.41,145.99 146.41,144.49zM129.47,137.03l3.39,4.07l4.75,-6.11l6.1,8.13h-18.98L129.47,137.03z"/>
-</vector>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
index 0462a93..5bcce4d 100644
--- a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -20,7 +20,7 @@
android:id="@+id/setup_wizard_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:icon="@drawable/ic_signal_cellular_alt_rounded_24px"
+ android:icon="@drawable/ic_signal_cellular_alt_rounded"
app:suwHeaderText="@string/install_carrier_app_title"
app:suwFooter="@layout/install_carrier_app_footer">
@@ -37,21 +37,6 @@
android:text="@string/install_carrier_app_description_default"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
-
- <com.android.setupwizardlib.view.FillContentLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1">
-
- <!-- TODO(b/72511181): final illo and content description update -->
- <ImageView
- android:src="@drawable/placeholder"
- style="@style/SuwContentIllustration"
- android:contentDescription="@null"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- </com.android.setupwizardlib.view.FillContentLayout>
</LinearLayout>
</com.android.setupwizardlib.GlifLayout>
diff --git a/packages/SimAppDialog/res/values/strings.xml b/packages/SimAppDialog/res/values/strings.xml
index 0c3930d..87941cb 100644
--- a/packages/SimAppDialog/res/values/strings.xml
+++ b/packages/SimAppDialog/res/values/strings.xml
@@ -15,22 +15,21 @@
limitations under the License.
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- TODO character limits -->
- <!-- The name of this application -->
+ <!-- The name of this application [CHAR LIMIT=NONE] -->
<string name="app_name">Sim App Dialog</string>
<!-- Install Carrier App Activity -->
- <!-- Title of screen asking user to download the carrier app to match the inserted SIM card -->
+ <!-- Title of screen asking user to download the carrier app to match the inserted SIM card [CHAR LIMIT=30] -->
<string name="install_carrier_app_title">Activate mobile service</string>
- <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we know the name of the carrier-->
+ <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we know the name of the carrier [CHAR LIMIT=90] -->
<string name="install_carrier_app_description">To get your new SIM working properly, you\'ll
need to install the <xliff:g name="carrier_name" example="Project Fi">%1$s</xliff:g> app
</string>
- <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we don't know the name of the carrier-->
+ <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we don't know the name of the carrier [CHAR LIMIT=100] -->
<string name="install_carrier_app_description_default">To get your new SIM working properly,
you\'ll need to install the carrier app
</string>
- <!-- Name of the button used to defer downloading the carrier app -->
+ <!-- Name of the button used to defer downloading the carrier app [CHAR LIMIT=25]-->
<string name="install_carrier_app_defer_action">Not now</string>
- <!-- Name of the button for downloading the carrier app -->
+ <!-- Name of the button for downloading the carrier app [CHAR LIMIT=25] -->
<string name="install_carrier_app_download_action">Download app</string>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d6fab4c..285b89f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -206,7 +206,7 @@
<uses-permission android:name="android.permission.WATCH_APPOPS" />
<!-- to read and change hvac values in a car -->
- <uses-permission android:name="android.car.permission.ADJUST_CAR_CLIMATE" />
+ <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
<application
android:name=".SystemUIApplication"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index addbf84..37de433 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -47,6 +47,8 @@
<dimen name="widget_title_font_size">24sp</dimen>
<!-- Slice subtitle -->
<dimen name="widget_label_font_size">16sp</dimen>
+ <!-- Slice offset when pulsing -->
+ <dimen name="widget_pulsing_bottom_padding">24dp</dimen>
<!-- Clock without header -->
<dimen name="widget_big_font_size">64dp</dimen>
<!-- Clock with header -->
diff --git a/packages/SystemUI/res/drawable/recents_onboarding_toast_rounded_background.xml b/packages/SystemUI/res/drawable/recents_onboarding_toast_rounded_background.xml
new file mode 100644
index 0000000..05db3a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_onboarding_toast_rounded_background.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="?android:attr/colorAccent" />
+ <corners android:radius="8dp" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/rounded_ripple.xml b/packages/SystemUI/res/drawable/rounded_ripple.xml
index 5588eb2..d9ed823 100644
--- a/packages/SystemUI/res/drawable/rounded_ripple.xml
+++ b/packages/SystemUI/res/drawable/rounded_ripple.xml
@@ -23,7 +23,7 @@
</item>
<item android:id="@android:id/background">
<shape android:shape="rectangle">
- <solid android:color="#FFFFFFFF"/>
+ <solid android:color="?android:attr/colorBackgroundFloating"/>
<corners android:radius="8dp"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index ef18725..f424171 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -38,9 +38,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="1dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
+ android:layout_marginStart="16dp"
android:layout_gravity="center_vertical"
android:gravity="end" >
@@ -48,18 +46,17 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
- android:layout_margin="15dp"
+ android:layout_marginEnd="8dp"
android:visibility="gone"
layout="@layout/mobile_signal_group" />
<com.android.keyguard.CarrierText
android:id="@+id/qs_carrier_text"
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_marginStart="8dp"
+ android:layout_gravity="center_vertical|start"
android:layout_marginEnd="32dp"
- android:gravity="center_vertical|start"
android:ellipsize="marquee"
android:textAppearance="@style/TextAppearance.QS.TileLabel"
android:textColor="?android:attr/textColorPrimary"
diff --git a/packages/SystemUI/res/layout/recents_onboarding.xml b/packages/SystemUI/res/layout/recents_onboarding.xml
index 12f278a..6764eee 100644
--- a/packages/SystemUI/res/layout/recents_onboarding.xml
+++ b/packages/SystemUI/res/layout/recents_onboarding.xml
@@ -14,32 +14,48 @@
limitations under the License.
-->
-<FrameLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="48dp"
- android:layout_width="match_parent"
- android:background="@android:color/black"
- android:layout_gravity="center">
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?android:attr/listDivider"
- android:gravity="top"/>
- <TextView
- android:id="@+id/onboarding_text"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingBottom="13dp"
+ android:orientation="vertical">
+
+ <LinearLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:textColor="@android:color/white"
- android:textSize="16sp"
- android:drawableBottom="@drawable/ic_chevron_up"/>
- <ImageView
- android:id="@+id/dismiss"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="12dp"
- android:layout_marginEnd="6dp"
- android:src="@drawable/ic_close_white"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:layout_gravity="center_vertical|end"/>
-</FrameLayout>
\ No newline at end of file
+ android:layout_height="40dp"
+ android:paddingStart="24dp"
+ android:paddingEnd="4dp"
+ android:background="@drawable/recents_onboarding_toast_rounded_background"
+ android:layout_gravity="center_horizontal"
+ android:elevation="2dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/onboarding_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textColor="@android:color/white"
+ android:textSize="16sp"/>
+ <ImageView
+ android:id="@+id/dismiss"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_gravity="center_vertical"
+ android:padding="10dp"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:alpha="0.7"
+ android:src="@drawable/ic_close_white"
+ android:background="?android:attr/selectableItemBackgroundBorderless"/>
+ </LinearLayout>
+
+ <View
+ android:id="@+id/arrow"
+ android:elevation="2dp"
+ android:layout_width="10dp"
+ android:layout_height="8dp"
+ android:layout_marginTop="-2dp"
+ android:layout_gravity="center_horizontal"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 84ca657..870aed4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -136,6 +136,10 @@
views where the distance can't be measured -->
<dimen name="notification_icon_appear_padding">15dp</dimen>
+ <!-- Vertical translation of the shelf during animation that happens after the
+ notification panel collapses -->
+ <dimen name="shelf_appear_translation">9dp</dimen>
+
<!-- The amount the content shifts upwards when transforming into the icon -->
<dimen name="notification_icon_transform_content_shift">32dp</dimen>
@@ -371,7 +375,7 @@
<dimen name="qs_header_alarm_icon_size">18dp</dimen>
<dimen name="qs_header_alarm_text_margin_start">6dp</dimen>
<dimen name="qs_footer_padding_start">16dp</dimen>
- <dimen name="qs_footer_padding_end">24dp</dimen>
+ <dimen name="qs_footer_padding_end">16dp</dimen>
<dimen name="qs_footer_icon_size">16dp</dimen>
<dimen name="qs_notif_collapsed_space">64dp</dimen>
@@ -841,6 +845,9 @@
<!-- The size of the drag hint text. -->
<dimen name="recents_drag_hint_text_size">14sp</dimen>
+ <!-- The size of corner radius of the arrow in the onboarding toast. -->
+ <dimen name="recents_onboarding_toast_arrow_corner_radius">2dp</dimen>
+
<!-- The min alpha to apply to a task affiliation group color. -->
<item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c1e1873..29f1335 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -405,7 +405,10 @@
<string name="accessibility_cell_data_on">Mobile Data On</string>
<!-- Content description of the cell data being disabled. [CHAR LIMIT=NONE] -->
- <string name="cell_data_off">Mobile data off</string>
+ <string name="cell_data_off_content_description">Mobile data off</string>
+
+ <!-- Content description of the cell data being disabled but shortened. [CHAR LIMIT=20] -->
+ <string name="cell_data_off">Off</string>
<!-- Content description of the bluetooth tethering icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_bluetooth_tether">Bluetooth tethering.</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java
new file mode 100644
index 0000000..b2b140e
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.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.systemui.shared.system;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.DisplayListCanvas;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.WindowCallbacks;
+
+public class WindowCallbacksCompat {
+
+ private final WindowCallbacks mWindowCallbacks = new WindowCallbacks() {
+ @Override
+ public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
+ Rect stableInsets) {
+ WindowCallbacksCompat.this.onWindowSizeIsChanging(newBounds, fullscreen, systemInsets,
+ stableInsets);
+ }
+
+ @Override
+ public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen,
+ Rect systemInsets, Rect stableInsets, int resizeMode) {
+ WindowCallbacksCompat.this.onWindowDragResizeStart(initialBounds, fullscreen,
+ systemInsets, stableInsets, resizeMode);
+ }
+
+ @Override
+ public void onWindowDragResizeEnd() {
+ WindowCallbacksCompat.this.onWindowDragResizeEnd();
+ }
+
+ @Override
+ public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
+ return WindowCallbacksCompat.this.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
+ }
+
+ @Override
+ public void onRequestDraw(boolean reportNextDraw) {
+ WindowCallbacksCompat.this.onRequestDraw(reportNextDraw);
+ }
+
+ @Override
+ public void onPostDraw(DisplayListCanvas canvas) {
+ WindowCallbacksCompat.this.onPostDraw(canvas);
+ }
+ };
+
+ private final View mView;
+
+ public WindowCallbacksCompat(View view) {
+ mView = view;
+ }
+
+ public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
+ Rect stableInsets) { }
+
+ public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
+ Rect stableInsets, int resizeMode) { }
+
+ public void onWindowDragResizeEnd() { }
+
+ public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
+ return false;
+ }
+
+ public void onRequestDraw(boolean reportNextDraw) {
+ if (reportNextDraw) {
+ reportDrawFinish();
+ }
+ }
+
+ public void onPostDraw(Canvas canvas) { }
+
+ public void reportDrawFinish() {
+ mView.getViewRootImpl().reportDrawFinish();
+ }
+
+ public boolean attach() {
+ ViewRootImpl root = mView.getViewRootImpl();
+ if (root != null) {
+ root.addWindowCallbacks(mWindowCallbacks);
+ root.requestInvalidateRootRenderNode();
+ return true;
+ }
+ return false;
+ }
+
+ public void detach() {
+ ViewRootImpl root = mView.getViewRootImpl();
+ if (root != null) {
+ root.removeWindowCallbacks(mWindowCallbacks);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index d63ad08..00cd5a7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -265,11 +265,11 @@
mPendingLockCheck.cancel(false);
mPendingLockCheck = null;
}
+ reset();
}
@Override
public void onResume(int reason) {
- reset();
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index c71c433..42c7a56 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -78,6 +78,11 @@
}
break;
}
+ case READY: {
+ mRemainingAttempts = -1;
+ resetState();
+ break;
+ }
default:
resetState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index a0fa69e..e54b083 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -14,6 +14,10 @@
package com.android.systemui;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -21,12 +25,14 @@
import static com.android.systemui.tuner.TunablePadding.FLAG_START;
import static com.android.systemui.tuner.TunablePadding.FLAG_END;
+import android.annotation.Dimension;
import android.app.Fragment;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
@@ -41,6 +47,7 @@
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.Surface;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
@@ -359,6 +366,7 @@
if (!mBoundingPath.isEmpty()) {
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setAntiAlias(true);
canvas.drawPath(mBoundingPath, mPaint);
}
}
@@ -388,7 +396,7 @@
if (hasCutout()) {
mBounds.set(mInfo.displayCutout.getBounds());
localBounds(mBoundingRect);
- mInfo.displayCutout.getBounds().getBoundaryPath(mBoundingPath);
+ updateBoundingPath();
invalidate();
newVisible = VISIBLE;
} else {
@@ -400,6 +408,44 @@
}
}
+ private void updateBoundingPath() {
+ int lw = mInfo.logicalWidth;
+ int lh = mInfo.logicalHeight;
+
+ boolean flipped = mInfo.rotation == ROTATION_90 || mInfo.rotation == ROTATION_270;
+
+ int dw = flipped ? lh : lw;
+ int dh = flipped ? lw : lh;
+
+ mBoundingPath.set(DisplayCutout.pathFromResources(getResources(), lw, lh));
+ Matrix m = new Matrix();
+ transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
+ mBoundingPath.transform(m);
+ }
+
+ private static void transformPhysicalToLogicalCoordinates(@Surface.Rotation int rotation,
+ @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
+ switch (rotation) {
+ case ROTATION_0:
+ out.reset();
+ break;
+ case ROTATION_90:
+ out.setRotate(270);
+ out.postTranslate(0, physicalWidth);
+ break;
+ case ROTATION_180:
+ out.setRotate(180);
+ out.postTranslate(physicalWidth, physicalHeight);
+ break;
+ case ROTATION_270:
+ out.setRotate(90);
+ out.postTranslate(physicalHeight, 0);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown rotation: " + rotation);
+ }
+ }
+
private boolean hasCutout() {
final DisplayCutout displayCutout = mInfo.displayCutout;
if (displayCutout == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index c01cafa..52d458c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.SmartReplyLogger;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
@@ -146,5 +147,6 @@
() -> new NotificationViewHierarchyManager(context));
providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
+ providers.put(SmartReplyLogger.class, () -> new SmartReplyLogger(context));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
index c238e54..a81043e 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
@@ -134,8 +134,11 @@
}
private void handleShowDialog(SomeArgs args) {
- if (DEBUG) Log.d(TAG, "handleShowDialog");
- if (mDialogShowing) {
+ if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: "
+ + mDialogView.isAnimatingAway());
+ if (mDialogView.isAnimatingAway()) {
+ mDialogView.forceRemove();
+ } else if (mDialogShowing) {
Log.w(TAG, "Dialog already showing");
return;
}
@@ -168,7 +171,7 @@
}
private void handleHideDialog(boolean userCanceled) {
- if (DEBUG) Log.d(TAG, "handleHideDialog");
+ if (DEBUG) Log.d(TAG, "handleHideDialog, userCanceled: " + userCanceled);
if (!mDialogShowing) {
// This can happen if there's a race and we get called from both
// onAuthenticated and onError, etc.
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
index d1d6609..8013a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricPrompt;
@@ -76,9 +75,29 @@
private Bundle mBundle;
private final LinearLayout mDialog;
private int mLastState;
+ private boolean mAnimatingAway;
+ private boolean mWasForceRemoved;
private final float mDisplayWidth;
+ private final Runnable mShowAnimationRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLayout.animate()
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION_SHOW)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ mDialog.animate()
+ .translationY(0)
+ .setDuration(ANIMATION_DURATION_SHOW)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ }
+ };
+
public FingerprintDialogView(Context context, Handler handler) {
super(context);
mHandler = handler;
@@ -192,26 +211,20 @@
positive.setVisibility(View.GONE);
}
- // Dim the background and slide the dialog up
- mDialog.setTranslationY(mAnimationTranslationOffset);
- mLayout.setAlpha(0f);
- postOnAnimation(new Runnable() {
- @Override
- public void run() {
- mLayout.animate()
- .alpha(1f)
- .setDuration(ANIMATION_DURATION_SHOW)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .start();
- mDialog.animate()
- .translationY(0)
- .setDuration(ANIMATION_DURATION_SHOW)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .start();
- }
- });
+ if (!mWasForceRemoved) {
+ // Dim the background and slide the dialog up
+ mDialog.setTranslationY(mAnimationTranslationOffset);
+ mLayout.setAlpha(0f);
+ postOnAnimation(mShowAnimationRunnable);
+ } else {
+ // Show the dialog immediately
+ mLayout.animate().cancel();
+ mDialog.animate().cancel();
+ mDialog.setAlpha(1.0f);
+ mDialog.setTranslationY(0);
+ mLayout.setAlpha(1.0f);
+ }
+ mWasForceRemoved = false;
}
private void setDismissesDialog(View v) {
@@ -224,10 +237,13 @@
}
public void startDismiss() {
+ mAnimatingAway = true;
+
final Runnable endActionRunnable = new Runnable() {
@Override
public void run() {
mWindowManager.removeView(FingerprintDialogView.this);
+ mAnimatingAway = false;
}
};
@@ -251,6 +267,23 @@
});
}
+ /**
+ * Force remove the window, cancelling any animation that's happening. This should only be
+ * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
+ * will cause the dialog to show without an animation the next time it's attached.
+ */
+ public void forceRemove() {
+ mLayout.animate().cancel();
+ mDialog.animate().cancel();
+ mWindowManager.removeView(FingerprintDialogView.this);
+ mAnimatingAway = false;
+ mWasForceRemoved = true;
+ }
+
+ public boolean isAnimatingAway() {
+ return mAnimatingAway;
+ }
+
public void setBundle(Bundle bundle) {
mBundle = bundle;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 9464105..ebd15f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -613,7 +613,7 @@
int bluetoothProfile) { }
@Override
- public void onProfileAudioStateChanged(int bluetoothProfile, int state) { }
+ public void onAudioModeChanged() { }
}
private final class BluetoothErrorListener implements Utils.ErrorListener {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d6e59c7..553b2ef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -357,7 +357,12 @@
// ActivityManagerService) will not reconstruct the keyguard if it is already showing.
synchronized (KeyguardViewMediator.this) {
resetKeyguardDonePendingLocked();
- resetStateLocked();
+ if (mLockPatternUtils.isLockScreenDisabled(userId)) {
+ // If we switching to a user that has keyguard disabled, dismiss keyguard.
+ dismiss(null /* callback */, null /* message */);
+ } else {
+ resetStateLocked();
+ }
adjustStatusBarLocked();
}
}
@@ -1184,6 +1189,10 @@
Trace.endSection();
}
+ public boolean isHiding() {
+ return mHiding;
+ }
+
/**
* Handles SET_OCCLUDED message sent by setOccluded()
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index cf549fa..28dd26f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -383,7 +383,7 @@
if (TextUtils.equals(mInfo.typeContentDescription,
mContext.getString(R.string.data_connection_no_internet))
|| TextUtils.equals(mInfo.typeContentDescription,
- mContext.getString(R.string.cell_data_off))) {
+ mContext.getString(R.string.cell_data_off_content_description))) {
contentDescription.append(mInfo.typeContentDescription);
}
mMobileSignal.setContentDescription(contentDescription);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 2abe9d9..d6182c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -108,20 +108,21 @@
}
if (mDataController.isMobileDataEnabled()) {
if (mKeyguardMonitor.isSecure() && !mKeyguardMonitor.canSkipBouncer()) {
- mActivityStarter.postQSRunnableDismissingKeyguard(this::showDisableDialog);
+ mActivityStarter.postQSRunnableDismissingKeyguard(this::maybeShowDisableDialog);
} else {
- if (Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, false)) {
- mDataController.setMobileDataEnabled(false);
- } else {
- mUiHandler.post(this::showDisableDialog);
- }
+ mUiHandler.post(this::maybeShowDisableDialog);
}
} else {
mDataController.setMobileDataEnabled(true);
}
}
- private void showDisableDialog() {
+ private void maybeShowDisableDialog() {
+ if (Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, false)) {
+ // Directly turn off mobile data if the user has seen the dialog before.
+ mDataController.setMobileDataEnabled(false);
+ return;
+ }
mHost.collapsePanels();
String carrierName = mController.getMobileDataNetworkName();
if (TextUtils.isEmpty(carrierName)) {
@@ -194,7 +195,18 @@
state.state = Tile.STATE_INACTIVE;
state.secondaryLabel = r.getString(R.string.cell_data_off);
}
- state.contentDescription = state.label + ", " + state.secondaryLabel;
+
+
+ // TODO(b/77881974): Instead of switching out the description via a string check for
+ // we need to have two strings provided by the MobileIconGroup.
+ final CharSequence contentDescriptionSuffix;
+ if (state.state == Tile.STATE_INACTIVE) {
+ contentDescriptionSuffix = r.getString(R.string.cell_data_off_content_description);
+ } else {
+ contentDescriptionSuffix = state.secondaryLabel;
+ }
+
+ state.contentDescription = state.label + ", " + contentDescriptionSuffix;
}
private CharSequence getMobileDataDescription(CallbackInfo cb) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 4b312f5..16c2a75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -143,26 +143,41 @@
public void showDetail(boolean show) {
int zenDuration = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZEN_DURATION, 0);
- switch (zenDuration) {
- case Settings.Global.ZEN_DURATION_PROMPT:
- mUiHandler.post(() -> {
- Dialog mDialog = new EnableZenModeDialog(mContext).createDialog();
- mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- SystemUIDialog.setShowForAllUsers(mDialog, true);
- SystemUIDialog.registerDismissListener(mDialog);
- SystemUIDialog.setWindowOnTop(mDialog);
- mUiHandler.post(() -> mDialog.show());
- mHost.collapsePanels();
- });
- break;
- case Settings.Global.ZEN_DURATION_FOREVER:
- mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
- break;
- default:
- Uri conditionId = ZenModeConfig.toTimeCondition(mContext, zenDuration,
- ActivityManager.getCurrentUser(), true).id;
- mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
- conditionId, TAG);
+ boolean showOnboarding = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
+ if (showOnboarding) {
+ // don't show on-boarding again or notification ever
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+ // turn on DND
+ mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
+ // show on-boarding screen
+ Intent intent = new Intent(Settings.ZEN_MODE_ONBOARDING);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
+ } else {
+ switch (zenDuration) {
+ case Settings.Global.ZEN_DURATION_PROMPT:
+ mUiHandler.post(() -> {
+ Dialog mDialog = new EnableZenModeDialog(mContext).createDialog();
+ mDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ SystemUIDialog.setShowForAllUsers(mDialog, true);
+ SystemUIDialog.registerDismissListener(mDialog);
+ SystemUIDialog.setWindowOnTop(mDialog);
+ mUiHandler.post(() -> mDialog.show());
+ mHost.collapsePanels();
+ });
+ break;
+ case Settings.Global.ZEN_DURATION_FOREVER:
+ mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
+ break;
+ default:
+ Uri conditionId = ZenModeConfig.toTimeCondition(mContext, zenDuration,
+ ActivityManager.getCurrentUser(), true).id;
+ mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ conditionId, TAG);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index 30e9afd8..0d8aed4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -24,15 +24,16 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.os.SystemProperties;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -76,17 +77,13 @@
private final View mLayout;
private final TextView mTextView;
private final ImageView mDismissView;
- private final ColorDrawable mBackgroundDrawable;
- private final int mDarkBackgroundColor;
- private final int mLightBackgroundColor;
- private final int mDarkContentColor;
- private final int mLightContentColor;
- private final RippleDrawable mDarkRipple;
- private final RippleDrawable mLightRipple;
+ private final View mArrowView;
+ private final int mOnboardingToastColor;
+ private final int mOnboardingToastArrowRadius;
+ private int mNavBarHeight;
private boolean mTaskListenerRegistered;
private boolean mLayoutAttachedToWindow;
- private boolean mBackgroundIsLight;
private int mLastTaskId;
private boolean mHasDismissed;
private int mNumAppsLaunchedSinceDismiss;
@@ -159,24 +156,30 @@
mLayout = LayoutInflater.from(mContext).inflate(R.layout.recents_onboarding, null);
mTextView = mLayout.findViewById(R.id.onboarding_text);
mDismissView = mLayout.findViewById(R.id.dismiss);
- mDarkBackgroundColor = res.getColor(android.R.color.background_dark);
- mLightBackgroundColor = res.getColor(android.R.color.background_light);
- mDarkContentColor = res.getColor(R.color.primary_text_default_material_light);
- mLightContentColor = res.getColor(R.color.primary_text_default_material_dark);
- mDarkRipple = new RippleDrawable(res.getColorStateList(R.color.ripple_material_light),
- null, null);
- mLightRipple = new RippleDrawable(res.getColorStateList(R.color.ripple_material_dark),
- null, null);
- mBackgroundDrawable = new ColorDrawable(mDarkBackgroundColor);
+ mArrowView = mLayout.findViewById(R.id.arrow);
+
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
+ mOnboardingToastColor = res.getColor(typedValue.resourceId);
+ mOnboardingToastArrowRadius = res.getDimensionPixelSize(
+ R.dimen.recents_onboarding_toast_arrow_corner_radius);
mLayout.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
- mLayout.setBackground(mBackgroundDrawable);
mDismissView.setOnClickListener(v -> {
hide(true);
mHasDismissed = true;
mNumAppsLaunchedSinceDismiss = 0;
});
+ ViewGroup.LayoutParams arrowLp = mArrowView.getLayoutParams();
+ ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+ arrowLp.width, arrowLp.height, false));
+ Paint arrowPaint = arrowDrawable.getPaint();
+ arrowPaint.setColor(mOnboardingToastColor);
+ // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
+ arrowPaint.setPathEffect(new CornerPathEffect(mOnboardingToastArrowRadius));
+ mArrowView.setBackground(arrowDrawable);
+
if (RESET_PREFS_FOR_DEBUG) {
Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, false);
Prefs.putInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, 0);
@@ -234,6 +237,7 @@
int orientation = mContext.getResources().getConfiguration().orientation;
if (!mLayoutAttachedToWindow && orientation == Configuration.ORIENTATION_PORTRAIT) {
mLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+
mWindowManager.addView(mLayout, getWindowLayoutParams());
int layoutHeight = mLayout.getHeight();
if (layoutHeight == 0) {
@@ -281,29 +285,18 @@
}
}
- public void setContentDarkIntensity(float contentDarkIntensity) {
- boolean backgroundIsLight = contentDarkIntensity > 0.5f;
- if (backgroundIsLight != mBackgroundIsLight) {
- mBackgroundIsLight = backgroundIsLight;
- mBackgroundDrawable.setColor(mBackgroundIsLight
- ? mLightBackgroundColor : mDarkBackgroundColor);
- int contentColor = mBackgroundIsLight ? mDarkContentColor : mLightContentColor;
- mTextView.setTextColor(contentColor);
- mTextView.getCompoundDrawables()[3].setColorFilter(contentColor,
- PorterDuff.Mode.SRC_IN);
- mDismissView.setColorFilter(contentColor);
- mDismissView.setBackground(mBackgroundIsLight ? mDarkRipple : mLightRipple);
- }
+ public void setNavBarHeight(int navBarHeight) {
+ mNavBarHeight = navBarHeight;
}
private WindowManager.LayoutParams getWindowLayoutParams() {
- int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+ int flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG,
+ 0, -mNavBarHeight / 2,
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
flags,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java b/packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java
new file mode 100644
index 0000000..de85c0f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.graphics.Outline;
+import android.graphics.Path;
+import android.graphics.drawable.shapes.PathShape;
+import android.support.annotation.NonNull;
+
+/**
+ * Wrapper around {@link android.graphics.drawable.shapes.PathShape}
+ * that creates a shape with a triangular path (pointing up or down).
+ */
+public class TriangleShape extends PathShape {
+ private Path mTriangularPath;
+
+ public TriangleShape(Path path, float stdWidth, float stdHeight) {
+ super(path, stdWidth, stdHeight);
+ mTriangularPath = path;
+ }
+
+ public static TriangleShape create(float width, float height, boolean isPointingUp) {
+ Path triangularPath = new Path();
+ if (isPointingUp) {
+ triangularPath.moveTo(0, height);
+ triangularPath.lineTo(width, height);
+ triangularPath.lineTo(width / 2, 0);
+ triangularPath.close();
+ } else {
+ triangularPath.moveTo(0, 0);
+ triangularPath.lineTo(width / 2, height);
+ triangularPath.lineTo(width, 0);
+ triangularPath.close();
+ }
+ return new TriangleShape(triangularPath, width, height);
+ }
+
+ @Override
+ public void getOutline(@NonNull Outline outline) {
+ outline.setConvexPath(mTriangularPath);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 29c2edc..4256cd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -80,7 +80,7 @@
private RemoteInputView mHeadsUpRemoteInput;
private SmartReplyConstants mSmartReplyConstants;
- private SmartReplyView mExpandedSmartReplyView;
+ private SmartReplyLogger mSmartReplyLogger;
private NotificationViewWrapper mContractedWrapper;
private NotificationViewWrapper mExpandedWrapper;
@@ -153,6 +153,7 @@
super(context, attrs);
mHybridGroupManager = new HybridGroupManager(getContext(), this);
mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
+ mSmartReplyLogger = Dependency.get(SmartReplyLogger.class);
initView();
}
@@ -1243,7 +1244,7 @@
}
applyRemoteInput(entry, hasRemoteInput);
- applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices);
+ applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry);
}
private void applyRemoteInput(NotificationData.Entry entry, boolean hasRemoteInput) {
@@ -1344,13 +1345,21 @@
return null;
}
- private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent) {
- mExpandedSmartReplyView = mExpandedChild == null ?
- null : applySmartReplyView(mExpandedChild, remoteInput, pendingIntent);
+ private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
+ NotificationData.Entry entry) {
+ if (mExpandedChild != null) {
+ SmartReplyView view =
+ applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry);
+ if (view != null && remoteInput != null && remoteInput.getChoices() != null
+ && remoteInput.getChoices().length > 0) {
+ mSmartReplyLogger.smartRepliesAdded(entry, remoteInput.getChoices().length);
+ }
+ }
}
private SmartReplyView applySmartReplyView(
- View view, RemoteInput remoteInput, PendingIntent pendingIntent) {
+ View view, RemoteInput remoteInput, PendingIntent pendingIntent,
+ NotificationData.Entry entry) {
View smartReplyContainerCandidate = view.findViewById(
com.android.internal.R.id.smart_reply_container);
if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
@@ -1372,7 +1381,8 @@
}
}
if (smartReplyView != null) {
- smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent);
+ smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
+ mSmartReplyLogger, entry);
smartReplyContainer.setVisibility(View.VISIBLE);
}
return smartReplyView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 0112661..6364f5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar;
import static com.android.systemui.statusbar.phone.NotificationIconContainer.IconState.NO_VALUE;
-import static com.android.systemui.statusbar.phone.NotificationIconContainer.OVERFLOW_EARLY_AMOUNT;
import android.content.Context;
import android.content.res.Configuration;
@@ -26,6 +25,7 @@
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.MathUtils;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
@@ -58,6 +58,8 @@
= SystemProperties.getBoolean("debug.icon_scroll_animations", true);
private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
private static final String TAG = "NotificationShelf";
+ private static final long SHELF_IN_TRANSLATION_DURATION = 220;
+
private ViewInvertHelper mViewInvertHelper;
private boolean mDark;
private NotificationIconContainer mShelfIcons;
@@ -65,6 +67,7 @@
private int[] mTmp = new int[2];
private boolean mHideBackground;
private int mIconAppearTopPadding;
+ private int mShelfAppearTranslation;
private int mStatusBarHeight;
private int mStatusBarPaddingStart;
private AmbientState mAmbientState;
@@ -120,6 +123,7 @@
mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
mStatusBarPaddingStart = res.getDimensionPixelOffset(R.dimen.status_bar_padding_start);
mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height);
+ mShelfAppearTranslation = res.getDimensionPixelSize(R.dimen.shelf_appear_translation);
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.height = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
@@ -151,6 +155,18 @@
updateInteractiveness();
}
+ public void fadeInTranslating() {
+ float translation = mShelfIcons.getTranslationY();
+ mShelfIcons.setTranslationY(translation + mShelfAppearTranslation);
+ mShelfIcons.setAlpha(0);
+ mShelfIcons.animate()
+ .alpha(1)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+ .translationY(translation)
+ .setDuration(SHELF_IN_TRANSLATION_DURATION)
+ .start();
+ }
+
@Override
protected View getContentView() {
return mShelfIcons;
@@ -175,12 +191,14 @@
float viewEnd = lastViewState.yTranslation + lastViewState.height;
mShelfState.copyFrom(lastViewState);
mShelfState.height = getIntrinsicHeight();
- mShelfState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
+
+ float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
getFullyClosedTranslation());
+ float darkTranslation = mAmbientState.getDarkTopPadding();
+ float yRatio = mAmbientState.hasPulsingNotifications() ?
+ 0 : mAmbientState.getDarkAmount();
+ mShelfState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
mShelfState.zTranslation = ambientState.getBaseZHeight();
- if (mAmbientState.isDark() && !mAmbientState.hasPulsingNotifications()) {
- mShelfState.yTranslation = mAmbientState.getDarkTopPadding();
- }
float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
/ (getIntrinsicHeight() * 2);
openedAmount = Math.min(1.0f, openedAmount);
@@ -555,7 +573,9 @@
iconState.translateContent = false;
}
float transitionAmount;
- if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
+ if (mAmbientState.getDarkAmount() > 0 && !row.isInShelf()) {
+ transitionAmount = mAmbientState.isFullyDark() ? 1 : 0;
+ } else if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
|| iconState.useLinearTransitionAmount) {
transitionAmount = iconTransitionAmount;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyLogger.java
new file mode 100644
index 0000000..75dd77d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyLogger.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import com.android.internal.statusbar.IStatusBarService;
+
+/**
+ * Handles reporting when smart replies are added to a notification
+ * and clicked upon.
+ */
+public class SmartReplyLogger {
+ protected IStatusBarService mBarService;
+
+ public SmartReplyLogger(Context context) {
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ public void smartReplySent(NotificationData.Entry entry, int replyIndex) {
+ try {
+ mBarService.onNotificationSmartReplySent(entry.notification.getKey(),
+ replyIndex);
+ } catch (RemoteException e) {
+ // Nothing to do, system going down
+ }
+ }
+
+ public void smartRepliesAdded(final NotificationData.Entry entry, int replyCount) {
+ try {
+ mBarService.onNotificationSmartRepliesAdded(entry.notification.getKey(),
+ replyCount);
+ } catch (RemoteException e) {
+ // Nothing to do, system going down
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index fb525f7..5a02d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -37,19 +37,20 @@
private final UserGridRecyclerView mUserGridView;
private final ProgressBar mSwitchingUsers;
private final int mShortAnimDuration;
+ private final StatusBar mStatusBar;
private boolean mShowing;
public FullscreenUserSwitcher(StatusBar statusBar, ViewStub containerStub, Context context) {
+ mStatusBar = statusBar;
mParent = containerStub.inflate();
mContainer = mParent.findViewById(R.id.container);
mUserGridView = mContainer.findViewById(R.id.user_grid);
- mUserGridView.setStatusBar(statusBar);
GridLayoutManager layoutManager = new GridLayoutManager(context,
context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
mUserGridView.setLayoutManager(layoutManager);
mUserGridView.buildAdapter();
- mUserGridView.setUserSelectionListener(record -> toggleSwitchInProgress(true));
+ mUserGridView.setUserSelectionListener(this::onUserSelected);
mShortAnimDuration = mContainer.getResources()
.getInteger(android.R.integer.config_shortAnimTime);
@@ -57,8 +58,35 @@
mSwitchingUsers = mParent.findViewById(R.id.switching_users);
}
+ public void show() {
+ if (mShowing) {
+ return;
+ }
+ mShowing = true;
+ mParent.setVisibility(View.VISIBLE);
+ }
+
+ public void hide() {
+ mShowing = false;
+ toggleSwitchInProgress(false);
+ mParent.setVisibility(View.GONE);
+ }
+
public void onUserSwitched(int newUserId) {
- mUserGridView.onUserSwitched(newUserId);
+ mParent.post(this::showOfflineAuthUi);
+ }
+
+ private void onUserSelected(UserGridRecyclerView.UserRecord record) {
+ if (record.mIsForeground) {
+ showOfflineAuthUi();
+ return;
+ }
+ toggleSwitchInProgress(true);
+ }
+
+ private void showOfflineAuthUi() {
+ mStatusBar.executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
+ true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
}
private void toggleSwitchInProgress(boolean inProgress) {
@@ -91,18 +119,4 @@
}
});
}
-
- public void show() {
- if (mShowing) {
- return;
- }
- mShowing = true;
- mParent.setVisibility(View.VISIBLE);
- }
-
- public void hide() {
- mShowing = false;
- toggleSwitchInProgress(false);
- mParent.setVisibility(View.GONE);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index e09a360..5ad08ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -38,8 +38,6 @@
import com.android.settingslib.users.UserManagerHelper;
import com.android.systemui.R;
-import com.android.systemui.qs.car.CarQSFragment;
-import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
import java.util.List;
@@ -50,8 +48,6 @@
*/
public class UserGridRecyclerView extends RecyclerView implements
UserManagerHelper.OnUsersUpdateListener {
-
- private StatusBar mStatusBar;
private UserSelectionListener mUserSelectionListener;
private UserAdapter mAdapter;
private UserManagerHelper mUserManagerHelper;
@@ -92,29 +88,22 @@
super.setAdapter(mAdapter);
}
- public void setStatusBar(@Nullable StatusBar statusBar) {
- mStatusBar = statusBar;
- }
-
private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) {
List<UserRecord> userRecords = new ArrayList<>();
for (UserInfo userInfo : userInfoList) {
- boolean isCurrent = false;
- if (ActivityManager.getCurrentUser() == userInfo.id) {
- isCurrent = true;
- }
+ boolean isForeground = mUserManagerHelper.getForegroundUserId() == userInfo.id;
UserRecord record = new UserRecord(userInfo, false /* isGuest */,
- false /* isAddUser */, isCurrent);
+ false /* isAddUser */, isForeground);
userRecords.add(record);
}
- // Add guest user record if the current user is not a guest
- if (!mUserManagerHelper.isGuestUser()) {
+ // Add guest user record if the foreground user is not a guest
+ if (!mUserManagerHelper.foregroundUserIsGuestUser()) {
userRecords.add(addGuestUserRecord());
}
- // Add add user record if the current user can add users
- if (mUserManagerHelper.canAddUsers()) {
+ // Add add user record if the foreground user can add users
+ if (mUserManagerHelper.foregroundUserCanAddUsers()) {
userRecords.add(addUserRecord());
}
@@ -128,7 +117,7 @@
UserInfo userInfo = new UserInfo();
userInfo.name = mContext.getString(R.string.car_guest);
return new UserRecord(userInfo, true /* isGuest */,
- false /* isAddUser */, false /* isCurrent */);
+ false /* isAddUser */, false /* isForeground */);
}
/**
@@ -138,26 +127,13 @@
UserInfo userInfo = new UserInfo();
userInfo.name = mContext.getString(R.string.car_add_user);
return new UserRecord(userInfo, false /* isGuest */,
- true /* isAddUser */, false /* isCurrent */);
- }
-
- public void onUserSwitched(int newUserId) {
- // Bring up security view after user switch is completed.
- post(this::showOfflineAuthUi);
+ true /* isAddUser */, false /* isForeground */);
}
public void setUserSelectionListener(UserSelectionListener userSelectionListener) {
mUserSelectionListener = userSelectionListener;
}
- void showOfflineAuthUi() {
- // TODO: Show keyguard UI in-place.
- if (mStatusBar != null) {
- mStatusBar.executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
- true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
- }
- }
-
@Override
public void onUsersUpdate() {
mAdapter.clearUsers();
@@ -332,21 +308,21 @@
/**
* Object wrapper class for the userInfo. Use it to distinguish if a profile is a
- * guest profile, add user profile, or a current user.
+ * guest profile, add user profile, or the foreground user.
*/
public static final class UserRecord {
public final UserInfo mInfo;
public final boolean mIsGuest;
public final boolean mIsAddUser;
- public final boolean mIsCurrent;
+ public final boolean mIsForeground;
public UserRecord(UserInfo userInfo, boolean isGuest, boolean isAddUser,
- boolean isCurrent) {
+ boolean isForeground) {
mInfo = userInfo;
mIsGuest = isGuest;
mIsAddUser = isAddUser;
- mIsCurrent = isCurrent;
+ mIsForeground = isForeground;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 3d7067d..1fb1ddd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -99,6 +99,11 @@
private int mBurnInPreventionOffsetY;
/**
+ * Clock vertical padding when pulsing.
+ */
+ private int mPulsingPadding;
+
+ /**
* Doze/AOD transition amount.
*/
private float mDarkAmount;
@@ -109,9 +114,9 @@
private boolean mCurrentlySecure;
/**
- * If notification panel view currently has a touch.
+ * Dozing and receiving a notification (AOD notification.)
*/
- private boolean mTracking;
+ private boolean mPulsing;
/**
* Distance in pixels between the top of the screen and the first view of the bouncer.
@@ -130,11 +135,13 @@
R.dimen.burn_in_prevention_offset_x);
mBurnInPreventionOffsetY = res.getDimensionPixelSize(
R.dimen.burn_in_prevention_offset_y);
+ mPulsingPadding = res.getDimensionPixelSize(
+ R.dimen.widget_pulsing_bottom_padding);
}
public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight,
- float dark, boolean secure, boolean tracking, int bouncerTop) {
+ float dark, boolean secure, boolean pulsing, int bouncerTop) {
mMinTopMargin = minTopMargin + mContainerTopPadding;
mMaxShadeBottom = maxShadeBottom;
mNotificationStackHeight = notificationStackHeight;
@@ -144,7 +151,7 @@
mKeyguardStatusHeight = keyguardStatusHeight;
mDarkAmount = dark;
mCurrentlySecure = secure;
- mTracking = tracking;
+ mPulsing = pulsing;
mBouncerTop = bouncerTop;
}
@@ -152,7 +159,7 @@
final int y = getClockY();
result.clockY = y;
result.clockAlpha = getClockAlpha(y);
- result.stackScrollerPadding = y + mKeyguardStatusHeight;
+ result.stackScrollerPadding = y + (mPulsing ? 0 : mKeyguardStatusHeight);
result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
}
@@ -194,9 +201,13 @@
private int getClockY() {
// Dark: Align the bottom edge of the clock at about half of the screen:
- final float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
- final float clockYRegular = getExpandedClockPosition();
- final boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop;
+ float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
+ if (mPulsing) {
+ clockYDark -= mPulsingPadding;
+ }
+
+ float clockYRegular = getExpandedClockPosition();
+ boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop;
float clockYTarget = mCurrentlySecure && hasEnoughSpace ?
mMinTopMargin : -mKeyguardStatusHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 58f8baa..ca6d596 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -103,6 +103,7 @@
import java.io.PrintWriter;
import java.util.List;
import java.util.Locale;
+import java.util.Optional;
/**
* Fragment containing the NavigationBarFragment. Contains logic for what happens
@@ -1109,8 +1110,11 @@
public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
// Only hide the icon if the top task changes its requestedOrientation
// Launcher can alter its requestedOrientation while it's not on top, don't hide on this
- final boolean top = ActivityManagerWrapper.getInstance().getRunningTask().id == taskId;
- if (top) setRotateSuggestionButtonState(false);
+ Optional.ofNullable(ActivityManagerWrapper.getInstance())
+ .map(ActivityManagerWrapper::getRunningTask)
+ .ifPresent(a -> {
+ if (a.id == taskId) setRotateSuggestionButtonState(false);
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index db2139d..14a36d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -812,9 +812,6 @@
if (mGestureHelper != null) {
mGestureHelper.onDarkIntensityChange(intensity);
}
- if (mRecentsOnboarding != null) {
- mRecentsOnboarding.setContentDarkIntensity(intensity);
- }
}
@Override
@@ -831,6 +828,7 @@
updateButtonLocationOnScreen(getHomeButton(), mHomeButtonBounds);
updateButtonLocationOnScreen(getRecentsButton(), mRecentsButtonBounds);
mGestureHelper.onLayout(changed, left, top, right, bottom);
+ mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
}
private void updateButtonLocationOnScreen(ButtonDispatcher button, Rect buttonBounds) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index b7af84a..351633b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -238,6 +238,7 @@
private boolean mIsFullWidth;
private float mDarkAmount;
private float mDarkAmountTarget;
+ private boolean mPulsing;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mNoVisibleNotifications = true;
private ValueAnimator mDarkAnimator;
@@ -477,7 +478,7 @@
mKeyguardStatusView.getHeight(),
mDarkAmount,
mStatusBar.isKeyguardCurrentlySecure(),
- mTracking,
+ mPulsing,
mBouncerTop);
mClockPositionAlgorithm.run(mClockPositionResult);
if (animate || mClockAnimator != null) {
@@ -2689,14 +2690,8 @@
positionClockAndNotifications();
}
- public void setNoVisibleNotifications(boolean noNotifications) {
- mNoVisibleNotifications = noNotifications;
- if (mQs != null) {
- mQs.setHasNotifications(!noNotifications);
- }
- }
-
public void setPulsing(boolean pulsing) {
+ mPulsing = pulsing;
mKeyguardStatusView.setPulsing(pulsing);
positionClockAndNotifications();
mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
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 ab13678..4b2bc45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3942,7 +3942,8 @@
}
private void showBouncerIfKeyguard() {
- if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+ if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
+ && !mKeyguardViewMediator.isHiding()) {
showBouncer(true /* animated */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 94db95a..cd17cfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -280,7 +280,7 @@
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
@Override
- public void onProfileAudioStateChanged(int bluetoothProfile, int state) {}
+ public void onAudioModeChanged() {}
private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
ActuallyCachedState state = mCachedState.get(device);
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 74b3926..4c79ee32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -23,6 +23,8 @@
import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.SmartReplyLogger;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import java.text.BreakIterator;
@@ -109,14 +111,16 @@
Math.max(getChildCount(), 1), DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR);
}
- public void setRepliesFromRemoteInput(RemoteInput remoteInput, PendingIntent pendingIntent) {
+ public void setRepliesFromRemoteInput(RemoteInput remoteInput, PendingIntent pendingIntent,
+ SmartReplyLogger smartReplyLogger, NotificationData.Entry entry) {
removeAllViews();
if (remoteInput != null && pendingIntent != null) {
CharSequence[] choices = remoteInput.getChoices();
if (choices != null) {
- for (CharSequence choice : choices) {
+ for (int i = 0; i < choices.length; ++i) {
Button replyButton = inflateReplyButton(
- getContext(), this, choice, remoteInput, pendingIntent);
+ getContext(), this, i, choices[i], remoteInput, pendingIntent,
+ smartReplyLogger, entry);
addView(replyButton);
}
}
@@ -130,8 +134,9 @@
}
@VisibleForTesting
- Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice,
- RemoteInput remoteInput, PendingIntent pendingIntent) {
+ Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
+ CharSequence choice, RemoteInput remoteInput, PendingIntent pendingIntent,
+ SmartReplyLogger smartReplyLogger, NotificationData.Entry entry) {
Button b = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_reply_button, root, false);
b.setText(choice);
@@ -147,6 +152,7 @@
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Unable to send smart reply", e);
}
+ smartReplyLogger.smartReplySent(entry, replyIndex);
return false; // do not defer
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index 7e6fe02..bd76820 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -208,7 +208,7 @@
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
- R.string.cell_data_off,
+ R.string.cell_data_off_content_description,
0,
false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 7c1c566..91a4b07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -70,8 +70,8 @@
private int mIntrinsicPadding;
private int mExpandAnimationTopChange;
private ExpandableNotificationRow mExpandingNotification;
- private boolean mFullyDark;
private int mDarkTopPadding;
+ private float mDarkAmount;
public AmbientState(Context context) {
reload(context);
@@ -149,6 +149,16 @@
mDark = dark;
}
+ /** Dark ratio of the status bar **/
+ public void setDarkAmount(float darkAmount) {
+ mDarkAmount = darkAmount;
+ }
+
+ /** Returns the dark ratio of the status bar */
+ public float getDarkAmount() {
+ return mDarkAmount;
+ }
+
public void setHideSensitive(boolean hideSensitive) {
mHideSensitive = hideSensitive;
}
@@ -413,17 +423,10 @@
}
/**
- * {@see isFullyDark}
- */
- public void setFullyDark(boolean fullyDark) {
- mFullyDark = fullyDark;
- }
-
- /**
* @return {@code true } when shade is completely dark: in AOD or ambient display.
*/
public boolean isFullyDark() {
- return mFullyDark;
+ return mDarkAmount == 1;
}
public void setDarkTopPadding(int darkTopPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index d282f25..bc5a848 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -96,6 +96,7 @@
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -269,8 +270,6 @@
*/
private boolean mOnlyScrollingInThisMotion;
private boolean mDisallowDismissInThisMotion;
- private boolean mInterceptDelegateEnabled;
- private boolean mDelegateToScrollView;
private boolean mDisallowScrollingInThisMotion;
private long mGoToFullShadeDelay;
private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
@@ -562,17 +561,17 @@
return;
}
- final int color;
- if (mAmbientState.isDark()) {
- color = Color.WHITE;
- } else {
- float alpha =
- BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
- alpha *= 1f - mDarkAmount;
- // We need to manually blend in the background color
- int scrimColor = mScrimController.getBackgroundColor();
- color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
- }
+ float alpha =
+ BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
+ alpha *= 1f - mDarkAmount;
+ // We need to manually blend in the background color.
+ int scrimColor = mScrimController.getBackgroundColor();
+ int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
+
+ // Interpolate between semi-transparent notification panel background color
+ // and white AOD separator.
+ float colorInterpolation = Interpolators.DECELERATE_QUINT.getInterpolation(mDarkAmount);
+ int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
if (mCachedBackgroundColor != color) {
mCachedBackgroundColor = color;
@@ -3961,13 +3960,18 @@
private void setDarkAmount(float darkAmount) {
mDarkAmount = darkAmount;
- final boolean fullyDark = darkAmount == 1;
- if (mAmbientState.isFullyDark() != fullyDark) {
- mAmbientState.setFullyDark(fullyDark);
+ boolean wasFullyDark = mAmbientState.isFullyDark();
+ mAmbientState.setDarkAmount(darkAmount);
+ if (mAmbientState.isFullyDark() != wasFullyDark) {
updateContentHeight();
+ DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
+ if (mAmbientState.isFullyDark() && dozeParameters.shouldControlScreenOff()) {
+ mShelf.fadeInTranslating();
+ }
}
updateBackgroundDimming();
updateAntiBurnInTranslation();
+ requestChildrenUpdate();
}
public float getDarkAmount() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index a8d2d98..f4d7f8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -276,8 +276,6 @@
if (i >= firstHiddenIndex) {
// we need normal padding now, to be in sync with what the stack calculates
lastView = null;
- ExpandableViewState viewState = resultState.getViewStateForView(v);
- viewState.hidden = true;
}
notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
float increasedPadding = v.getIncreasedPaddingAmount();
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 5a4478f..639e49b 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -58,7 +58,7 @@
private static final String TUNER_VERSION = "sysui_tuner_version";
- private static final int CURRENT_TUNER_VERSION = 2;
+ private static final int CURRENT_TUNER_VERSION = 3;
private final Observer mObserver = new Observer();
// Map of Uris we listen on to their settings keys.
@@ -119,6 +119,10 @@
if (oldVersion < 2) {
setTunerEnabled(mContext, false);
}
+ if (oldVersion < 3) {
+ // Delay this so that we can wait for everything to be registered first.
+ new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(() -> clearAll(), 5000);
+ }
setValue(TUNER_VERSION, newVersion);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 2c85bb6..ca55e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -52,26 +52,28 @@
public static final int EVENT_MUTE_CHANGED = 15; // (stream|int) (muted|bool)
public static final int EVENT_TOUCH_LEVEL_DONE = 16; // (stream|int) (level|bool)
public static final int EVENT_ZEN_CONFIG_CHANGED = 17; // (allow/disallow|string)
+ public static final int EVENT_RINGER_TOGGLE = 18; // (ringer_mode)
private static final String[] EVENT_TAGS = {
- "show_dialog",
- "dismiss_dialog",
- "active_stream_changed",
- "expand",
- "key",
- "collection_started",
- "collection_stopped",
- "icon_click",
- "settings_click",
- "touch_level_changed",
- "level_changed",
- "internal_ringer_mode_changed",
- "external_ringer_mode_changed",
- "zen_mode_changed",
- "suppressor_changed",
- "mute_changed",
- "touch_level_done",
- "zen_mode_config_changed",
+ "show_dialog",
+ "dismiss_dialog",
+ "active_stream_changed",
+ "expand",
+ "key",
+ "collection_started",
+ "collection_stopped",
+ "icon_click",
+ "settings_click",
+ "touch_level_changed",
+ "level_changed",
+ "internal_ringer_mode_changed",
+ "external_ringer_mode_changed",
+ "zen_mode_changed",
+ "suppressor_changed",
+ "mute_changed",
+ "touch_level_done",
+ "zen_mode_config_changed",
+ "ringer_toggle"
};
public static final int DISMISS_REASON_UNKNOWN = 0;
@@ -112,6 +114,7 @@
public static Callback sCallback;
public static void writeEvent(Context context, int tag, Object... list) {
+ MetricsLogger logger = new MetricsLogger();
final long time = System.currentTimeMillis();
final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[tag]);
if (list != null && list.length > 0) {
@@ -139,7 +142,7 @@
break;
case EVENT_ICON_CLICK:
MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_ICON,
- (Integer) list[1]);
+ (Integer) list[0]);
sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
.append(iconStateToString((Integer) list[1]));
break;
@@ -155,10 +158,16 @@
break;
case EVENT_KEY:
MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_KEY,
- (Integer) list[1]);
+ (Integer) list[0]);
sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
.append(list[1]);
break;
+ case EVENT_RINGER_TOGGLE:
+ logger.action(MetricsEvent.ACTION_VOLUME_RINGER_TOGGLE, (Integer) list[0]);
+ break;
+ case EVENT_SETTINGS_CLICK:
+ logger.action(MetricsEvent.ACTION_VOLUME_SETTINGS);
+ break;
case EVENT_EXTERNAL_RINGER_MODE_CHANGED:
MetricsLogger.action(context, MetricsEvent.ACTION_RINGER_MODE,
(Integer) list[0]);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index a2d2615..00874e3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -301,15 +301,8 @@
if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream);
VolumeRow row = new VolumeRow();
initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
- if (dynamic && mRows.size() > 2) {
- // Dynamic Streams should be the first in the list, so they're shown to start of
- // everything except a11y
- mDialogRowsView.addView(row.view, 1);
- mRows.add(1, row);
- } else {
- mDialogRowsView.addView(row.view);
- mRows.add(row);
- }
+ mDialogRowsView.addView(row.view);
+ mRows.add(row);
}
private void addExistingRows() {
@@ -422,6 +415,7 @@
mSettingsView.setVisibility(
mDeviceProvisionedController.isDeviceProvisioned() ? VISIBLE : GONE);
mSettingsIcon.setOnClickListener(v -> {
+ Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK);
Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
dismissH(DISMISS_REASON_SETTINGS_CLICKED);
@@ -431,8 +425,6 @@
public void initRingerH() {
mRingerIcon.setOnClickListener(v -> {
- Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, AudioManager.STREAM_RING,
- mRingerIcon.getTag());
Prefs.putBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true);
final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
if (ss == null) {
@@ -456,6 +448,7 @@
mController.setStreamVolume(AudioManager.STREAM_RING, 1);
}
}
+ Events.writeEvent(mContext, Events.EVENT_RINGER_TOGGLE, newRingerMode);
updateRingerH();
provideTouchFeedbackH(newRingerMode);
mController.setRingerMode(newRingerMode, false);
@@ -603,7 +596,8 @@
return activeRow.stream == STREAM_RING
|| activeRow.stream == STREAM_ALARM
|| activeRow.stream == STREAM_VOICE_CALL
- || activeRow.stream == STREAM_ACCESSIBILITY;
+ || activeRow.stream == STREAM_ACCESSIBILITY
+ || mDynamic.get(activeRow.stream);
}
return false;
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 56882c6..2bb8106 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -22,11 +22,16 @@
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
+import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -38,6 +43,8 @@
import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.SmartReplyLogger;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import java.util.concurrent.atomic.AtomicReference;
@@ -46,6 +53,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -66,8 +75,12 @@
private int mDoubleLinePaddingHorizontal;
private int mSpacing;
+ @Mock private SmartReplyLogger mLogger;
+ private NotificationData.Entry mEntry;
+
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
mReceiver = new BlockingQueueIntentReceiver();
mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
mDependency.get(KeyguardDismissUtil.class).setDismissHandler(
@@ -82,6 +95,10 @@
mDoubleLinePaddingHorizontal = res.getDimensionPixelSize(
R.dimen.smart_reply_button_padding_horizontal_double_line);
mSpacing = res.getDimensionPixelSize(R.dimen.smart_reply_button_spacing);
+
+ StatusBarNotification notification = mock(StatusBarNotification.class);
+ when(notification.getKey()).thenReturn("akey");
+ mEntry = new NotificationData.Entry(notification);
}
@After
@@ -138,6 +155,13 @@
}
@Test
+ public void testSendSmartReply_LoggerCall() {
+ setRepliesFromRemoteInput(TEST_CHOICES);
+ mView.getChildAt(2).performClick();
+ verify(mLogger).smartReplySent(mEntry, 2);
+ }
+
+ @Test
public void testMeasure_empty() {
mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
assertEquals(500, mView.getMeasuredWidthAndState());
@@ -316,7 +340,7 @@
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(TEST_ACTION), 0);
RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
- mView.setRepliesFromRemoteInput(input, pendingIntent);
+ mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry);
}
/** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
@@ -343,8 +367,9 @@
}
Button previous = null;
- for (CharSequence choice : choices) {
- Button current = mView.inflateReplyButton(mContext, mView, choice, null, null);
+ for (int i = 0; i < choices.length; ++i) {
+ Button current = mView.inflateReplyButton(mContext, mView, i, choices[i],
+ null, null, null, null);
current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
current.getPaddingBottom());
if (previous != null) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index cd3031b..dd2b581 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -97,6 +97,7 @@
doNothing().when(mGroupManager).collapseAllGroups();
doNothing().when(mExpandHelper).cancelImmediately();
doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
+ doNothing().when(notificationShelf).fadeInTranslating();
}
@Test
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 446c0a1..daf4e9b 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5613,6 +5613,38 @@
// OS: P
SETTINGS_ZEN_ONBOARDING = 1380;
+ // OPEN: Settings > Display > Auto brightness
+ // CATEGORY: SETTINGS
+ // OS: P
+ SETTINGS_AUTO_BRIGHTNESS = 1381;
+
+ // OPEN: Smart replies in a notification seen at least once
+ // CATEGORY: NOTIFICATION
+ // PACKAGE: App that posted the notification
+ // SUBTYPE: Number of smart replies.
+ // OS: P
+ SMART_REPLY_VISIBLE = 1382;
+
+ // ACTION: Smart reply in a notification clicked.
+ // CATEGORY: NOTIFICATION
+ // PACKAGE: App that posted the notification
+ // SUBTYPE: Index of smart reply clicked.
+ // OS: P
+ SMART_REPLY_ACTION = 1383;
+
+ // Tagged data for SMART_REPLY_VISIBLE. Count of number of smart replies.
+ // OS: P
+ NOTIFICATION_SMART_REPLY_COUNT = 1384;
+
+ // Volume dialog > ringer toggle
+ // OS: P
+ ACTION_VOLUME_RINGER_TOGGLE = 1385;
+
+ // Volume dialog > settings button
+ // OS: P
+ ACTION_VOLUME_SETTINGS = 1386;
+
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index a2de8e7..fbec5cb 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -64,8 +64,7 @@
try {
File restoreData = prepareRestoreData(mInfo, mInFD);
- // TODO: version ?
- invokeAgentForAdbRestore(mAgent, mInfo, restoreData, 0);
+ invokeAgentForAdbRestore(mAgent, mInfo, restoreData);
} catch (IOException e) {
e.printStackTrace();
}
@@ -83,8 +82,8 @@
return sortedDataName;
}
- private void invokeAgentForAdbRestore(IBackupAgent agent, FileMetadata info, File restoreData,
- int versionCode) throws IOException {
+ private void invokeAgentForAdbRestore(IBackupAgent agent, FileMetadata info, File restoreData)
+ throws IOException {
String pkg = info.packageName;
File newStateName = new File(mDataDir, pkg + ".new");
try {
@@ -95,9 +94,9 @@
if (DEBUG) {
Slog.i(TAG, "Starting restore of package " + pkg + " for version code "
- + versionCode);
+ + info.version);
}
- agent.doRestore(backupData, versionCode, newState, mToken,
+ agent.doRestore(backupData, info.version, newState, mToken,
mBackupManagerService.getBackupManagerBinder());
} catch (IOException e) {
Slog.e(TAG, "Exception opening file. " + e);
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index dc28cd1..e4ce62d 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -27,6 +27,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.util.Slog;
@@ -240,12 +241,13 @@
PackageManager.GET_SIGNING_CERTIFICATES);
homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
homeVersion = homeInfo.getLongVersionCode();
- Signature[][] signingHistory = homeInfo.signingCertificateHistory;
- if (signingHistory == null || signingHistory.length == 0) {
- Slog.e(TAG, "Home app has no signing history");
+ SigningInfo signingInfo = homeInfo.signingInfo;
+ if (signingInfo == null) {
+ Slog.e(TAG, "Home app has no signing information");
} else {
// retrieve the newest sigs to back up
- Signature[] homeInfoSignatures = signingHistory[signingHistory.length - 1];
+ // TODO (b/73988180) use entire signing history in case of rollbacks
+ Signature[] homeInfoSignatures = signingInfo.getApkContentsSigners();
homeSigHashes = BackupUtils.hashSignatureArray(homeInfoSignatures);
}
} catch (NameNotFoundException e) {
@@ -334,8 +336,8 @@
}
}
- Signature[][] signingHistory = info.signingCertificateHistory;
- if (signingHistory == null || signingHistory.length == 0) {
+ SigningInfo signingInfo = info.signingInfo;
+ if (signingInfo == null) {
Slog.w(TAG, "Not backing up package " + packName
+ " since it appears to have no signatures.");
continue;
@@ -358,7 +360,7 @@
outputBufferStream.writeInt(info.versionCode);
}
// retrieve the newest sigs to back up
- Signature[] infoSignatures = signingHistory[signingHistory.length - 1];
+ Signature[] infoSignatures = signingInfo.getApkContentsSigners();
writeSignatureHashArray(outputBufferStream,
BackupUtils.hashSignatureArray(infoSignatures));
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 77163d3..0c99b44 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -99,6 +99,7 @@
private FullBackupObbConnection mObbConnection = null;
private ParcelFileDescriptor[] mPipes = null;
private byte[] mWidgetData = null;
+ private long mAppVersion;
private long mBytes;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
@@ -476,6 +477,9 @@
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
info);
+ // readAppManifestAndReturnSignatures() will have extracted the version from
+ // the manifest, so we save it to use in key-value restore later.
+ mAppVersion = info.version;
PackageManagerInternal pmi = LocalServices.getService(
PackageManagerInternal.class);
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
@@ -667,6 +671,8 @@
Slog.d(TAG, "Restoring key-value file for " + pkg
+ " : " + info.path);
}
+ // Set the version saved from manifest entry.
+ info.version = mAppVersion;
KeyValueAdbRestoreEngine restoreEngine =
new KeyValueAdbRestoreEngine(
mBackupManagerService,
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index 5518374..c39cceb 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.os.Process;
import android.util.Slog;
@@ -203,15 +204,16 @@
return false;
}
- Signature[][] deviceHistorySigs = target.signingCertificateHistory;
- if (ArrayUtils.isEmpty(deviceHistorySigs)) {
- Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
+ SigningInfo signingInfo = target.signingInfo;
+ if (signingInfo == null) {
+ Slog.w(TAG, "signingInfo is empty, app was either unsigned or the flag" +
" PackageManager#GET_SIGNING_CERTIFICATES was not specified");
return false;
}
if (DEBUG) {
- Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceHistorySigs);
+ Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device="
+ + signingInfo.getApkContentsSigners());
}
final int nStored = storedSigs.length;
@@ -225,8 +227,8 @@
} else {
// the app couldn't have rotated keys, since it was signed with multiple sigs - do
// a check to see if we find a match for all stored sigs
- // since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
- Signature[] deviceSigs = deviceHistorySigs[0];
+ // since app hasn't rotated key, we only need to check with its current signers
+ Signature[] deviceSigs = signingInfo.getApkContentsSigners();
int nDevice = deviceSigs.length;
// ensure that each stored sig matches an on-device sig
diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
index 994d5a96..a3d5601 100644
--- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.util.Slog;
@@ -106,12 +107,13 @@
printer.println(withApk ? "1" : "0");
// write the signature block
- Signature[][] signingHistory = pkg.signingCertificateHistory;
- if (signingHistory == null) {
+ SigningInfo signingInfo = pkg.signingInfo;
+ if (signingInfo == null) {
printer.println("0");
} else {
// retrieve the newest sigs to write
- Signature[] signatures = signingHistory[signingHistory.length - 1];
+ // TODO (b/73988180) use entire signing history in case of rollbacks
+ Signature[] signatures = signingInfo.getApkContentsSigners();
printer.println(Integer.toString(signatures.length));
for (Signature sig : signatures) {
printer.println(sig.toCharsString());
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 8ce4e64..6f4ae15 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -88,6 +88,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.LocalLog;
+import com.android.internal.util.StatLogger;
import com.android.server.AppStateTracker.Listener;
import java.io.ByteArrayOutputStream;
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index cec4f1a..23c57797 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -55,6 +55,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.StatLogger;
import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage;
import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
diff --git a/services/core/java/com/android/server/StatLogger.java b/services/core/java/com/android/server/StatLogger.java
deleted file mode 100644
index d85810d..0000000
--- a/services/core/java/com/android/server/StatLogger.java
+++ /dev/null
@@ -1,120 +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;
-
-import android.os.SystemClock;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.StatLoggerProto.Event;
-
-import java.io.PrintWriter;
-
-/**
- * Simple class to keep track of the number of times certain events happened and their durations for
- * benchmarking.
- *
- * TODO Update shortcut service to switch to it.
- *
- * @hide
- */
-public class StatLogger {
- private static final String TAG = "StatLogger";
-
- private final Object mLock = new Object();
-
- private final int SIZE;
-
- @GuardedBy("mLock")
- private final int[] mCountStats;
-
- @GuardedBy("mLock")
- private final long[] mDurationStats;
-
- private final String[] mLabels;
-
- public StatLogger(String[] eventLabels) {
- SIZE = eventLabels.length;
- mCountStats = new int[SIZE];
- mDurationStats = new long[SIZE];
- mLabels = eventLabels;
- }
-
- /**
- * Return the current time in the internal time unit.
- * Call it before an event happens, and
- * give it back to the {@link #logDurationStat(int, long)}} after the event.
- */
- public long getTime() {
- return SystemClock.elapsedRealtimeNanos() / 1000;
- }
-
- /**
- * @see {@link #getTime()}
- */
- public void logDurationStat(int eventId, long start) {
- synchronized (mLock) {
- if (eventId >= 0 && eventId < SIZE) {
- mCountStats[eventId]++;
- mDurationStats[eventId] += (getTime() - start);
- } else {
- Slog.wtf(TAG, "Invalid event ID: " + eventId);
- }
- }
- }
-
- @Deprecated
- public void dump(PrintWriter pw, String prefix) {
- dump(new IndentingPrintWriter(pw, " ").setIndent(prefix));
- }
-
- public void dump(IndentingPrintWriter pw) {
- synchronized (mLock) {
- pw.println("Stats:");
- pw.increaseIndent();
- for (int i = 0; i < SIZE; i++) {
- final int count = mCountStats[i];
- final double durationMs = mDurationStats[i] / 1000.0;
- pw.println(String.format("%s: count=%d, total=%.1fms, avg=%.3fms",
- mLabels[i], count, durationMs,
- (count == 0 ? 0 : ((double) durationMs) / count)));
- }
- pw.decreaseIndent();
- }
- }
-
- public void dumpProto(ProtoOutputStream proto, long fieldId) {
- synchronized (mLock) {
- final long outer = proto.start(fieldId);
-
- for (int i = 0; i < mLabels.length; i++) {
- final long inner = proto.start(StatLoggerProto.EVENTS);
-
- proto.write(Event.EVENT_ID, i);
- proto.write(Event.LABEL, mLabels[i]);
- proto.write(Event.COUNT, mCountStats[i]);
- proto.write(Event.TOTAL_DURATION_MICROS, mDurationStats[i]);
-
- proto.end(inner);
- }
-
- proto.end(outer);
- }
- }
-}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 04d54e1..2064309 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -767,7 +767,7 @@
/**
* The controller for all operations related to locktask.
*/
- final LockTaskController mLockTaskController;
+ private final LockTaskController mLockTaskController;
final UserController mUserController;
@@ -2902,6 +2902,7 @@
private boolean mBlacklistDisabled;
private String mExemptionsStr;
private List<String> mExemptions = Collections.emptyList();
+ private int mLogSampleRate = -1;
@HiddenApiEnforcementPolicy private int mPolicyPreP = HIDDEN_API_ENFORCEMENT_DEFAULT;
@HiddenApiEnforcementPolicy private int mPolicyP = HIDDEN_API_ENFORCEMENT_DEFAULT;
@@ -2916,6 +2917,10 @@
false,
this);
mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE),
+ false,
+ this);
+ mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS),
false,
this);
@@ -2942,6 +2947,15 @@
}
zygoteProcess.setApiBlacklistExemptions(mExemptions);
}
+ int logSampleRate = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, -1);
+ if (logSampleRate < 0 || logSampleRate > 0x10000) {
+ logSampleRate = -1;
+ }
+ if (logSampleRate != -1 && logSampleRate != mLogSampleRate) {
+ mLogSampleRate = logSampleRate;
+ zygoteProcess.setHiddenApiAccessLogSampleRate(mLogSampleRate);
+ }
mPolicyPreP = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS);
mPolicyP = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY_P_APPS);
}
@@ -5292,7 +5306,7 @@
synchronized (this) {
mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition
? REORDER_MOVE_TO_ORIGINAL_POSITION
- : REORDER_KEEP_IN_PLACE);
+ : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation");
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -12441,6 +12455,10 @@
return mActivityStartController;
}
+ LockTaskController getLockTaskController() {
+ return mLockTaskController;
+ }
+
ClientLifecycleManager getLifecycleManager() {
return mLifecycleManager;
}
@@ -17038,6 +17056,11 @@
pw.println("ms");
}
mUidObservers.finishBroadcast();
+
+ pw.println();
+ pw.println(" ServiceManager statistics:");
+ ServiceManager.sStatLogger.dump(pw, " ");
+ pw.println();
}
}
pw.println(" mForceBackgroundCheck=" + mForceBackgroundCheck);
@@ -26663,7 +26686,7 @@
record.waitingForNetwork = false;
final long totalTime = SystemClock.uptimeMillis() - startTime;
if (totalTime >= mWaitForNetworkTimeoutMs || DEBUG_NETWORK) {
- Slog.wtf(TAG_NETWORK, "Total time waited for network rules to get updated: "
+ Slog.w(TAG_NETWORK, "Total time waited for network rules to get updated: "
+ totalTime + ". Uid: " + callingUid + " procStateSeq: "
+ procStateSeq + " UidRec: " + record
+ " validateUidRec: " + mValidateUids.get(callingUid));
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index e539bf8..f32717a 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -779,11 +779,13 @@
* @param task The new parent {@link TaskRecord}.
*/
void setTask(TaskRecord task) {
- setTask(task, false /*reparenting*/);
+ setTask(task /* task */, false /* reparenting */);
}
/**
* This method should only be called by {@link TaskRecord#removeActivity(ActivityRecord)}.
+ * @param task The new parent task.
+ * @param reparenting Whether we're in the middle of reparenting.
*/
void setTask(TaskRecord task, boolean reparenting) {
// Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}.
@@ -791,12 +793,19 @@
return;
}
- final ActivityStack stack = getStack();
+ final ActivityStack oldStack = getStack();
+ final ActivityStack newStack = task != null ? task.getStack() : null;
- // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this
- // {@link ActivityRecord} from its current {@link ActivityStack}.
- if (!reparenting && stack != null && (task == null || stack != task.getStack())) {
- stack.onActivityRemovedFromStack(this);
+ // Inform old stack (if present) of activity removal and new stack (if set) of activity
+ // addition.
+ if (oldStack != newStack) {
+ if (!reparenting && oldStack != null) {
+ oldStack.onActivityRemovedFromStack(this);
+ }
+
+ if (newStack != null) {
+ newStack.onActivityAddedToStack(this);
+ }
}
this.task = task;
@@ -1073,8 +1082,15 @@
// Must reparent first in window manager
mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
+ // Reparenting prevents informing the parent stack of activity removal in the case that
+ // the new stack has the same parent. we must manually signal here if this is not the case.
+ final ActivityStack prevStack = prevTask.getStack();
+
+ if (prevStack != newTask.getStack()) {
+ prevStack.onActivityRemovedFromStack(this);
+ }
// Remove the activity from the old task and add it to the new task.
- prevTask.removeActivity(this, true /*reparenting*/);
+ prevTask.removeActivity(this, true /* reparenting */);
newTask.addActivityAtIndex(position, this);
}
@@ -1198,10 +1214,7 @@
}
boolean isFocusable() {
- if (inSplitScreenPrimaryWindowingMode() && mStackSupervisor.mIsDockMinimized) {
- return false;
- }
- return getWindowConfiguration().canReceiveKeys() || isAlwaysFocusable();
+ return mStackSupervisor.isFocusable(this, isAlwaysFocusable());
}
boolean isResizeable() {
@@ -1589,14 +1602,20 @@
void pauseKeyDispatchingLocked() {
if (!keysPaused) {
keysPaused = true;
- mWindowContainerController.pauseKeyDispatching();
+
+ if (mWindowContainerController != null) {
+ mWindowContainerController.pauseKeyDispatching();
+ }
}
}
void resumeKeyDispatchingLocked() {
if (keysPaused) {
keysPaused = false;
- mWindowContainerController.resumeKeyDispatching();
+
+ if (mWindowContainerController != null) {
+ mWindowContainerController.resumeKeyDispatching();
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 649d577..eb482c1 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -489,13 +489,13 @@
*/
void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
if (record == mResumedActivity && state != RESUMED) {
- clearResumedActivity(reason + " - onActivityStateChanged");
+ setResumedActivity(null, reason + " - onActivityStateChanged");
}
if (state == RESUMED) {
if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
+ reason);
- mResumedActivity = record;
+ setResumedActivity(record, reason + " - onActivityStateChanged");
mService.setResumedActivityUncheckLocked(record, reason);
mStackSupervisor.mRecentTasks.add(record.getTask());
}
@@ -1077,13 +1077,8 @@
}
boolean isFocusable() {
- if (getWindowConfiguration().canReceiveKeys()) {
- return true;
- }
- // The stack isn't focusable. See if its top activity is focusable to force focus on the
- // stack.
final ActivityRecord r = topRunningActivityLocked();
- return r != null && r.isFocusable();
+ return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
}
final boolean isAttached() {
@@ -2314,14 +2309,14 @@
return mResumedActivity;
}
- /**
- * Clears reference to currently resumed activity.
- */
- private void clearResumedActivity(String reason) {
- if (DEBUG_STACK) Slog.d(TAG_STACK, "clearResumedActivity: " + mResumedActivity + " reason:"
- + reason);
+ private void setResumedActivity(ActivityRecord r, String reason) {
+ if (mResumedActivity == r) {
+ return;
+ }
- mResumedActivity = null;
+ if (DEBUG_STACK) Slog.d(TAG_STACK, "setResumedActivity stack:" + this + " + from: "
+ + mResumedActivity + " to:" + r + " reason:" + reason);
+ mResumedActivity = r;
}
@GuardedBy("mService")
@@ -3743,7 +3738,7 @@
}
if (endTask) {
- mService.mLockTaskController.clearLockedTask(task);
+ mService.getLockTaskController().clearLockedTask(task);
}
} else if (!r.isState(PAUSING)) {
// If the activity is PAUSING, we will complete the finish once
@@ -4027,14 +4022,20 @@
* an activity moves away from the stack.
*/
void onActivityRemovedFromStack(ActivityRecord r) {
- if (mResumedActivity == r) {
- clearResumedActivity("onActivityRemovedFromStack");
+ removeTimeoutsForActivityLocked(r);
+
+ if (mResumedActivity != null && mResumedActivity == r) {
+ setResumedActivity(null, "onActivityRemovedFromStack");
}
- if (mPausingActivity == r) {
+ if (mPausingActivity != null && mPausingActivity == r) {
mPausingActivity = null;
}
+ }
- removeTimeoutsForActivityLocked(r);
+ void onActivityAddedToStack(ActivityRecord r) {
+ if(r.getState() == RESUMED) {
+ setResumedActivity(r, "onActivityAddedToStack");
+ }
}
/**
@@ -4639,7 +4640,7 @@
// In LockTask mode, moving a locked task to the back of the stack may expose unlocked
// ones. Therefore we need to check if this operation is allowed.
- if (!mService.mLockTaskController.canMoveTaskToBack(tr)) {
+ if (!mService.getLockTaskController().canMoveTaskToBack(tr)) {
return false;
}
@@ -4752,30 +4753,32 @@
mTmpBounds.clear();
mTmpInsetBounds.clear();
- for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
- final TaskRecord task = mTaskHistory.get(i);
- if (task.isResizeable()) {
- if (inFreeformWindowingMode()) {
- // TODO: Can be removed now since each freeform task is in 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.
- mTmpRect2.set(task.getOverrideBounds());
- fitWithinBounds(mTmpRect2, bounds);
- task.updateOverrideConfiguration(mTmpRect2);
- } else {
- task.updateOverrideConfiguration(taskBounds, insetBounds);
+ synchronized (mWindowManager.getWindowManagerLock()) {
+ for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
+ final TaskRecord task = mTaskHistory.get(i);
+ if (task.isResizeable()) {
+ if (inFreeformWindowingMode()) {
+ // TODO: Can be removed now since each freeform task is in 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.
+ mTmpRect2.set(task.getOverrideBounds());
+ fitWithinBounds(mTmpRect2, bounds);
+ task.updateOverrideConfiguration(mTmpRect2);
+ } else {
+ task.updateOverrideConfiguration(taskBounds, insetBounds);
+ }
+ }
+
+ mTmpBounds.put(task.taskId, task.getOverrideBounds());
+ if (tempTaskInsetBounds != null) {
+ mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
}
}
- mTmpBounds.put(task.taskId, task.getOverrideBounds());
- if (tempTaskInsetBounds != null) {
- mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
- }
+ mWindowContainerController.resize(bounds, mTmpBounds, mTmpInsetBounds);
+ setBounds(bounds);
}
-
- mWindowContainerController.resize(bounds, mTmpBounds, mTmpInsetBounds);
- setBounds(bounds);
}
@@ -5084,7 +5087,12 @@
onActivityRemovedFromStack(record);
}
- mTaskHistory.remove(task);
+ final boolean removed = mTaskHistory.remove(task);
+
+ if (removed) {
+ EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.taskId, getStackId());
+ }
+
removeActivitiesFromLRUListLocked(task);
updateTaskMovement(task, true);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e9a1358..cbf30bd 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -667,6 +667,14 @@
return mFocusedStack;
}
+ boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
+ if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
+ return false;
+ }
+
+ return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
+ }
+
ActivityStack getLastStack() {
return mLastFocusedStack;
}
@@ -1372,12 +1380,13 @@
mService.updateLruProcessLocked(app, true, null);
mService.updateOomAdjLocked();
+ final LockTaskController lockTaskController = mService.getLockTaskController();
if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
|| task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
|| (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED
- && mService.mLockTaskController.getLockTaskModeState()
- == LOCK_TASK_MODE_LOCKED)) {
- mService.mLockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
+ && lockTaskController.getLockTaskModeState()
+ == LOCK_TASK_MODE_LOCKED)) {
+ lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
}
try {
@@ -2900,7 +2909,7 @@
if (tr != null) {
tr.removeTaskActivitiesLocked(pauseImmediately, reason);
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
- mService.mLockTaskController.clearLockedTask(tr);
+ mService.getLockTaskController().clearLockedTask(tr);
if (tr.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
@@ -3814,7 +3823,7 @@
pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
getKeyguardController().dump(pw, prefix);
- mService.mLockTaskController.dump(pw, prefix);
+ mService.getLockTaskController().dump(pw, prefix);
}
public void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 6ca8a92..5b6b508 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -234,8 +234,9 @@
private Intent createSuspendedAppInterceptIntent(String suspendedPackage,
String suspendingPackage, String dialogMessage, int userId) {
final Intent interceptIntent = new Intent(mServiceContext, SuspendedAppActivity.class)
- .putExtra(Intent.EXTRA_PACKAGE_NAME, suspendedPackage)
+ .putExtra(SuspendedAppActivity.EXTRA_SUSPENDED_PACKAGE, suspendedPackage)
.putExtra(SuspendedAppActivity.EXTRA_DIALOG_MESSAGE, dialogMessage)
+ .putExtra(SuspendedAppActivity.EXTRA_SUSPENDING_PACKAGE, suspendingPackage)
.putExtra(Intent.EXTRA_USER_ID, userId)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 5337566..fb89e67 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1156,9 +1156,10 @@
// If we are not able to proceed, disassociate the activity from the task. Leaving an
// activity in an incomplete state can lead to issues, such as performing operations
// without a window container.
- if (!ActivityManager.isStartResultSuccessful(result)
- && mStartActivity.getTask() != null) {
- mStartActivity.getTask().removeActivity(mStartActivity);
+ final ActivityStack stack = mStartActivity.getStack();
+ if (!ActivityManager.isStartResultSuccessful(result) && stack != null) {
+ stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
+ null /* intentResultData */, "startActivity", true /* oomAdj */);
}
mService.mWindowManager.continueSurfaceLayout();
}
@@ -1208,7 +1209,7 @@
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
// still needs to be a lock task mode violation since the task gets cleared out and
// the device would otherwise leave the locked task.
- if (mService.mLockTaskController.isLockTaskModeViolation(reusedActivity.getTask(),
+ if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
@@ -2020,7 +2021,7 @@
mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
}
- if (mService.mLockTaskController.isLockTaskModeViolation(mStartActivity.getTask())) {
+ if (mService.getLockTaskController().isLockTaskModeViolation(mStartActivity.getTask())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
@@ -2043,7 +2044,7 @@
}
private int setTaskFromSourceRecord() {
- if (mService.mLockTaskController.isLockTaskModeViolation(mSourceRecord.getTask())) {
+ if (mService.getLockTaskController().isLockTaskModeViolation(mSourceRecord.getTask())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
@@ -2137,7 +2138,7 @@
private int setTaskFromInTask() {
// The caller is asking that the new activity be started in an explicit
// task it has provided to us.
- if (mService.mLockTaskController.isLockTaskModeViolation(mInTask)) {
+ if (mService.getLockTaskController().isLockTaskModeViolation(mInTask)) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
diff --git a/services/core/java/com/android/server/am/AppWarnings.java b/services/core/java/com/android/server/am/AppWarnings.java
index ab1d7bf..ea0251e2 100644
--- a/services/core/java/com/android/server/am/AppWarnings.java
+++ b/services/core/java/com/android/server/am/AppWarnings.java
@@ -122,8 +122,9 @@
return;
}
- if (ActivityManager.isRunningInTestHarness()
- && !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(r.realActivity)) {
+ // TODO(b/77862563): temp. fix while P is being finalized. To be reverted
+ if (/*ActivityManager.isRunningInTestHarness()
+ &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(r.realActivity)) {
// Don't show warning if we are running in a test harness and we don't have to always
// show for this activity.
return;
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 9caef4a..40b9e4f 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -133,4 +133,7 @@
# The activity's onStart has been called.
30059 am_on_start_called (User|1|5),(Component Name|3),(Reason|3)
# The activity's onDestroy has been called.
-30060 am_on_destroy_called (User|1|5),(Component Name|3),(Reason|3)
\ No newline at end of file
+30060 am_on_destroy_called (User|1|5),(Component Name|3),(Reason|3)
+
+# The task is being removed from its parent stack
+30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 2b988d3..a20452b 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -523,7 +523,7 @@
}
for (int i = mTasks.size() - 1; i >= 0; --i) {
final TaskRecord tr = mTasks.get(i);
- if (tr.userId == userId && !mService.mLockTaskController.isTaskWhitelisted(tr)) {
+ if (tr.userId == userId && !mService.getLockTaskController().isTaskWhitelisted(tr)) {
remove(tr);
}
}
@@ -1156,7 +1156,7 @@
}
// If we're in lock task mode, ignore the root task
- if (task == mService.mLockTaskController.getRootTask()) {
+ if (task == mService.getLockTaskController().getRootTask()) {
return false;
}
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index 17eeb5b..06b5e20 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -46,6 +46,8 @@
*/
class RecentsAnimation implements RecentsAnimationCallbacks {
private static final String TAG = RecentsAnimation.class.getSimpleName();
+ // TODO (b/73188263): Reset debugging flags
+ private static final boolean DEBUG = true;
private final ActivityManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
@@ -74,10 +76,13 @@
void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
ComponentName recentsComponent, int recentsUid) {
+ if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + intent);
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
if (!mWindowManager.canStartRecentsAnimation()) {
notifyAnimationCancelBeforeStart(recentsAnimationRunner);
+ if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
+ + mWindowManager.getPendingAppTransition());
return;
}
@@ -97,6 +102,7 @@
mRestoreTargetBehindStack = display.getStackAbove(targetStack);
if (mRestoreTargetBehindStack == null) {
notifyAnimationCancelBeforeStart(recentsAnimationRunner);
+ if (DEBUG) Slog.d(TAG, "No stack above target stack=" + targetStack);
return;
}
}
@@ -119,6 +125,8 @@
// Move the recents activity into place for the animation if it is not top most
display = targetActivity.getDisplay();
display.moveStackBehindBottomMostVisibleStack(targetStack);
+ if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
+ + display.getStackAbove(targetStack));
} else {
// No recents activity
ActivityOptions options = ActivityOptions.makeBasic();
@@ -140,6 +148,8 @@
display = targetActivity.getDisplay();
// TODO: Maybe wait for app to draw in this particular case?
+
+ if (DEBUG) Slog.d(TAG, "Started intent=" + intent);
}
// Mark the target activity as launch-behind to bump its visibility for the
@@ -148,7 +158,8 @@
// Fetch all the surface controls and pass them to the client to get the animation
// started
- mWindowManager.cancelRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ mWindowManager.cancelRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION,
+ "startRecentsActivity");
mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
this, display.mDisplayId, mStackSupervisor.mRecentTasks.getRecentTaskIds());
@@ -158,6 +169,9 @@
mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
targetActivity);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to start recents activity", e);
+ throw e;
} finally {
mWindowManager.continueSurfaceLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -167,6 +181,9 @@
@Override
public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode) {
synchronized (mService) {
+ if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
+ + mWindowManager.getRecentsAnimationController()
+ + " reorderMode=" + reorderMode);
if (mWindowManager.getRecentsAnimationController() == null) return;
// Just to be sure end the launch hint in case the target activity was never launched.
@@ -187,6 +204,9 @@
final ActivityStack targetStack = mDefaultDisplay.getStack(
WINDOWING_MODE_UNDEFINED, mTargetActivityType);
final ActivityRecord targetActivity = targetStack.getTopActivity();
+ if (DEBUG) Slog.d(TAG, "onAnimationFinished(): targetStack=" + targetStack
+ + " targetActivity=" + targetActivity
+ + " mRestoreTargetBehindStack=" + mRestoreTargetBehindStack);
if (targetActivity == null) {
return;
}
@@ -198,10 +218,27 @@
// Bring the target stack to the front
mStackSupervisor.mNoAnimActivities.add(targetActivity);
targetStack.moveToFront("RecentsAnimation.onAnimationFinished()");
+ if (DEBUG) {
+ final ActivityStack topStack = getTopNonAlwaysOnTopStack();
+ if (topStack != targetStack) {
+ Slog.w(TAG, "Expected target stack=" + targetStack
+ + " to be top most but found stack=" + topStack);
+ }
+ }
} else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){
// Restore the target stack to its previous position
final ActivityDisplay display = targetActivity.getDisplay();
display.moveStackBehindStack(targetStack, mRestoreTargetBehindStack);
+ if (DEBUG) {
+ final ActivityStack aboveTargetStack =
+ mDefaultDisplay.getStackAbove(targetStack);
+ if (mRestoreTargetBehindStack != null
+ && aboveTargetStack != mRestoreTargetBehindStack) {
+ Slog.w(TAG, "Expected target stack=" + targetStack
+ + " to restored behind stack=" + mRestoreTargetBehindStack
+ + " but it is behind stack=" + aboveTargetStack);
+ }
+ }
} else {
// Keep target stack in place, nothing changes, so ignore the transition
// logic below
@@ -221,6 +258,9 @@
// split-screen), or we will have returned to the app, and the minimized state
// should be reset
mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to clean up recents activity", e);
+ throw e;
} finally {
mWindowManager.continueSurfaceLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -239,4 +279,18 @@
Slog.e(TAG, "Failed to cancel recents animation before start", e);
}
}
+
+ /**
+ * @return The top stack that is not always-on-top.
+ */
+ private ActivityStack getTopNonAlwaysOnTopStack() {
+ for (int i = mDefaultDisplay.getChildCount() - 1; i >= 0; i--) {
+ final ActivityStack s = mDefaultDisplay.getChildAt(i);
+ if (s.getWindowConfiguration().isAlwaysOnTop()) {
+ continue;
+ }
+ return s;
+ }
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index ac6f01f..2de75273 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -210,7 +210,7 @@
// Check if someone tries to launch an unwhitelisted activity into LockTask mode.
final boolean lockTaskMode = options.getLockTaskMode();
if (aInfo != null && lockTaskMode
- && !supervisor.mService.mLockTaskController.isPackageWhitelisted(
+ && !supervisor.mService.getLockTaskController().isPackageWhitelisted(
UserHandle.getUserId(callingUid), aInfo.packageName)) {
final String msg = "Permission Denial: starting " + getIntentString(intent)
+ " from " + callerApp + " (pid=" + callingPid
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 034cb2e..0e418ad 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -451,7 +451,7 @@
}
void removeWindowContainer() {
- mService.mLockTaskController.clearLockedTask(this);
+ mService.getLockTaskController().clearLockedTask(this);
mWindowContainerController.removeContainer();
if (!getWindowConfiguration().persistTaskBounds()) {
// Reset current bounds for task whose bounds shouldn't be persisted so it uses
@@ -927,7 +927,26 @@
if (stack != null && !stack.isInStackLocked(this)) {
throw new IllegalStateException("Task must be added as a Stack child first.");
}
+ final ActivityStack oldStack = mStack;
mStack = stack;
+
+ // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this
+ // {@link ActivityRecord} from its current {@link ActivityStack}.
+
+ if (oldStack != mStack) {
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord activity = getChildAt(i);
+
+ if (oldStack != null) {
+ oldStack.onActivityRemovedFromStack(activity);
+ }
+
+ if (mStack != null) {
+ stack.onActivityAddedToStack(activity);
+ }
+ }
+ }
+
onParentChanged();
}
@@ -1232,6 +1251,7 @@
index = Math.min(size, index);
mActivities.add(index, r);
+
updateEffectiveIntent();
if (r.isPersistable()) {
mService.notifyTaskPersisterLocked(this, false);
@@ -1257,7 +1277,7 @@
* @return true if this was the last activity in the task.
*/
boolean removeActivity(ActivityRecord r) {
- return removeActivity(r, false /*reparenting*/);
+ return removeActivity(r, false /* reparenting */);
}
boolean removeActivity(ActivityRecord r, boolean reparenting) {
@@ -1266,7 +1286,7 @@
"Activity=" + r + " does not belong to task=" + this);
}
- r.setTask(null /*task*/, reparenting);
+ r.setTask(null /* task */, reparenting /* reparenting */);
if (mActivities.remove(r) && r.fullscreen) {
// Was previously in list.
@@ -1446,9 +1466,10 @@
}
final String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
+ final LockTaskController lockTaskController = mService.getLockTaskController();
switch (r.lockTaskLaunchMode) {
case LOCK_TASK_LAUNCH_MODE_DEFAULT:
- mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg)
+ mLockTaskAuth = lockTaskController.isPackageWhitelisted(userId, pkg)
? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
break;
@@ -1461,7 +1482,7 @@
break;
case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
- mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg)
+ mLockTaskAuth = lockTaskController.isPackageWhitelisted(userId, pkg)
? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
break;
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b6e414a2..fecb934 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2215,7 +2215,7 @@
protected void clearAllLockedTasks(String reason) {
synchronized (mService) {
- mService.mLockTaskController.clearLockedTasks(reason);
+ mService.getLockTaskController().clearLockedTasks(reason);
}
}
diff --git a/services/core/java/com/android/server/backup/BackupUtils.java b/services/core/java/com/android/server/backup/BackupUtils.java
index d817534..96c5621 100644
--- a/services/core/java/com/android/server/backup/BackupUtils.java
+++ b/services/core/java/com/android/server/backup/BackupUtils.java
@@ -20,6 +20,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
@@ -55,16 +56,16 @@
return false;
}
- Signature[][] deviceHistorySigs = target.signingCertificateHistory;
- if (ArrayUtils.isEmpty(deviceHistorySigs)) {
- Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
+ SigningInfo signingInfo = target.signingInfo;
+ if (signingInfo == null) {
+ Slog.w(TAG, "signingInfo is empty, app was either unsigned or the flag" +
" PackageManager#GET_SIGNING_CERTIFICATES was not specified");
return false;
}
if (DEBUG) {
Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
- + " device=" + deviceHistorySigs);
+ + " device=" + signingInfo.getApkContentsSigners());
}
final int nStored = storedSigHashes.size();
@@ -78,8 +79,9 @@
} else {
// the app couldn't have rotated keys, since it was signed with multiple sigs - do
// a check to see if we find a match for all stored sigs
- // since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
- ArrayList<byte[]> deviceHashes = hashSignatureArray(deviceHistorySigs[0]);
+ // since app hasn't rotated key, we only need to check with current signers
+ ArrayList<byte[]> deviceHashes =
+ hashSignatureArray(signingInfo.getApkContentsSigners());
int nDevice = deviceHashes.size();
// ensure that each stored sig matches an on-device sig
for (int i = 0; i < nStored; i++) {
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 906a6a3..6fa999c 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -24,6 +24,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
@@ -55,12 +56,14 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.TelephonyManager;
+import android.util.DataUnit;
import android.util.DebugUtils;
import android.util.Pair;
import android.util.Range;
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -71,6 +74,7 @@
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
+import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -96,9 +100,11 @@
private final Clock mClock;
private final Dependencies mDeps;
private final ContentResolver mResolver;
- private final SettingsObserver mSettingsObserver;
private final ConfigChangeReceiver mConfigChangeReceiver;
+ @VisibleForTesting
+ final ContentObserver mSettingsObserver;
+
private ConnectivityManager mCM;
private NetworkPolicyManager mNPM;
private NetworkStatsManager mStatsManager;
@@ -288,12 +294,16 @@
final NetworkPolicy[] policies = mNPM.getNetworkPolicies();
for (NetworkPolicy policy : policies) {
- if (hasActiveCycle(policy) && policy.template.matches(identity)) {
+ if (policy.hasCycle() && policy.template.matches(identity)) {
+ final long cycleStart = policy.cycleIterator().next().getLower()
+ .toInstant().toEpochMilli();
// Prefer user-defined warning, otherwise use hard limit
- final long policyBytes = (policy.warningBytes == LIMIT_DISABLED)
- ? policy.limitBytes : policy.warningBytes;
+ final long activeWarning = getActiveWarning(policy, cycleStart);
+ final long policyBytes = (activeWarning == WARNING_DISABLED)
+ ? getActiveLimit(policy, cycleStart)
+ : activeWarning;
- if (policyBytes != LIMIT_DISABLED) {
+ if (policyBytes != LIMIT_DISABLED && policyBytes != WARNING_DISABLED) {
final long policyBudget = getRemainingDailyBudget(policyBytes,
policy.cycleIterator().next());
minQuota = Math.min(minQuota, policyBudget);
@@ -324,11 +334,11 @@
if (DBG) Slog.d(TAG, "Setting quota: " + quota + " bytes");
}
+ // TODO: re-register if day changed: budget may have run out but should be refreshed.
if (haveMultipathBudget() && quota == mQuota) {
- // If we already have a usage callback pending , there's no need to re-register it
+ // If there is already a usage callback pending , there's no need to re-register it
// if the quota hasn't changed. The callback will simply fire as expected when the
- // budget is spent. Also: if we re-register the callback when we're below the
- // UsageCallback's minimum value of 2MB, we'll overshoot the budget.
+ // budget is spent.
if (DBG) Slog.d(TAG, "Quota still " + quota + ", not updating.");
return;
}
@@ -338,7 +348,17 @@
// ourselves any budget to work with.
final long usage = getDailyNonDefaultDataUsage();
final long budget = (usage == -1) ? 0 : Math.max(0, quota - usage);
- if (budget > 0) {
+
+ // Only consider budgets greater than MIN_THRESHOLD_BYTES, otherwise the callback will
+ // fire late, after data usage went over budget. Also budget should be 0 if remaining
+ // data is close to 0.
+ // This is necessary because the usage callback does not accept smaller thresholds.
+ // Because it snaps everything to MIN_THRESHOLD_BYTES, the lesser of the two evils is
+ // to snap to 0 here.
+ // This will only be called if the total quota for the day changed, not if usage changed
+ // since last time, so even if this is called very often the budget will not snap to 0
+ // as soon as there are less than 2MB left for today.
+ if (budget > NetworkStatsManager.MIN_THRESHOLD_BYTES) {
if (DBG) Slog.d(TAG, "Setting callback for " + budget +
" bytes on network " + network);
registerUsageCallback(budget);
@@ -388,9 +408,16 @@
}
}
- private static boolean hasActiveCycle(NetworkPolicy policy) {
- return policy.hasCycle() && policy.lastLimitSnooze <
- policy.cycleIterator().next().getLower().toInstant().toEpochMilli();
+ private static long getActiveWarning(NetworkPolicy policy, long cycleStart) {
+ return policy.lastWarningSnooze < cycleStart
+ ? policy.warningBytes
+ : WARNING_DISABLED;
+ }
+
+ private static long getActiveLimit(NetworkPolicy policy, long cycleStart) {
+ return policy.lastLimitSnooze < cycleStart
+ ? policy.limitBytes
+ : LIMIT_DISABLED;
}
// Only ever updated on the handler thread. Accessed from other binder threads to retrieve
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d37dd18..df6a6f8 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -249,6 +249,7 @@
"CarrierConfigChangeListener", mContext, smHandler, filter,
(Intent ignored) -> {
mLog.log("OBSERVED carrier config change");
+ updateConfiguration();
reevaluateSimCardProvisioning();
});
// TODO: Remove SimChangeListener altogether. For now, we retain it
@@ -261,28 +262,35 @@
});
mStateReceiver = new StateReceiver();
- filter = new IntentFilter();
+
+ // Load tethering configuration.
+ updateConfiguration();
+
+ startStateMachineUpdaters();
+ }
+
+ private void startStateMachineUpdaters() {
+ mCarrierConfigChange.startListening();
+
+ final Handler handler = mTetherMasterSM.getHandler();
+ IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_STATE);
filter.addAction(CONNECTIVITY_ACTION);
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
+ mContext.registerReceiver(mStateReceiver, filter, null, handler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
filter.addDataScheme("file");
- mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
+ mContext.registerReceiver(mStateReceiver, filter, null, handler);
- UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
-
- // this check is useful only for some unit tests; example: ConnectivityServiceTest
- if (userManager != null) {
- userManager.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
+ final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+ // This check is useful only for some unit tests; example: ConnectivityServiceTest.
+ if (umi != null) {
+ umi.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
}
-
- // load device config info
- updateConfiguration();
}
private WifiManager getWifiManager() {
@@ -384,17 +392,15 @@
*/
@VisibleForTesting
protected boolean isTetherProvisioningRequired() {
- String[] provisionApp = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app);
+ final TetheringConfiguration cfg = mConfig;
if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
- || provisionApp == null) {
+ || cfg.provisioningApp.length == 0) {
return false;
}
-
if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
return false;
}
- return (provisionApp.length == 2);
+ return (cfg.provisioningApp.length == 2);
}
// The logic here is aimed solely at confirming that a CarrierConfig exists
@@ -417,20 +423,6 @@
return !isEntitlementCheckRequired;
}
- // Used by the SIM card change observation code.
- // TODO: De-duplicate above code.
- private boolean hasMobileHotspotProvisionApp() {
- try {
- if (!mContext.getResources().getString(com.android.internal.R.string.
- config_mobile_hotspot_provision_app_no_ui).isEmpty()) {
- Log.d(TAG, "re-evaluate provisioning");
- return true;
- }
- } catch (Resources.NotFoundException e) {}
- Log.d(TAG, "no prov-check needed for new SIM");
- return false;
- }
-
/**
* Enables or disables tethering for the given type. This should only be called once
* provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
@@ -1187,7 +1179,7 @@
}
private void reevaluateSimCardProvisioning() {
- if (!hasMobileHotspotProvisionApp()) return;
+ if (!mConfig.hasMobileHotspotProvisionApp()) return;
if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
ArrayList<Integer> tethered = new ArrayList<>();
@@ -1546,7 +1538,6 @@
return;
}
- mCarrierConfigChange.startListening();
mSimChange.startListening();
mUpstreamNetworkMonitor.start();
@@ -1564,7 +1555,6 @@
mOffload.stop();
mUpstreamNetworkMonitor.stop();
mSimChange.stopListening();
- mCarrierConfigChange.stopListening();
notifyDownstreamsOfNewUpstreamIface(null);
handleNewUpstreamNetworkState(null);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 09bce7f..454c579 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -21,14 +21,23 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
+import static com.android.internal.R.array.config_tether_bluetooth_regexs;
+import static com.android.internal.R.array.config_tether_dhcp_range;
+import static com.android.internal.R.array.config_tether_usb_regexs;
+import static com.android.internal.R.array.config_tether_upstream_types;
+import static com.android.internal.R.array.config_tether_wifi_regexs;
+import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
-import android.telephony.TelephonyManager;
import android.net.util.SharedLog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.R;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -51,6 +60,8 @@
public class TetheringConfiguration {
private static final String TAG = TetheringConfiguration.class.getSimpleName();
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
@VisibleForTesting
public static final int DUN_NOT_REQUIRED = 0;
public static final int DUN_REQUIRED = 1;
@@ -79,18 +90,18 @@
public final String[] dhcpRanges;
public final String[] defaultIPv4DNS;
+ public final String[] provisioningApp;
+ public final String provisioningAppNoUi;
+
public TetheringConfiguration(Context ctx, SharedLog log) {
final SharedLog configLog = log.forSubComponent("config");
- tetherableUsbRegexs = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_usb_regexs);
+ tetherableUsbRegexs = getResourceStringArray(ctx, config_tether_usb_regexs);
// TODO: Evaluate deleting this altogether now that Wi-Fi always passes
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
- tetherableWifiRegexs = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_wifi_regexs);
- tetherableBluetoothRegexs = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_bluetooth_regexs);
+ tetherableWifiRegexs = getResourceStringArray(ctx, config_tether_wifi_regexs);
+ tetherableBluetoothRegexs = getResourceStringArray(ctx, config_tether_bluetooth_regexs);
dunCheck = checkDunRequired(ctx);
configLog.log("DUN check returned: " + dunCheckString(dunCheck));
@@ -101,6 +112,9 @@
dhcpRanges = getDhcpRanges(ctx);
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
+ provisioningApp = getResourceStringArray(ctx, config_mobile_hotspot_provision_app);
+ provisioningAppNoUi = getProvisioningAppNoUi(ctx);
+
configLog.log(toString());
}
@@ -116,6 +130,10 @@
return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
}
+ public boolean hasMobileHotspotProvisionApp() {
+ return !TextUtils.isEmpty(provisioningAppNoUi);
+ }
+
public void dump(PrintWriter pw) {
dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
@@ -129,6 +147,10 @@
dumpStringArray(pw, "dhcpRanges", dhcpRanges);
dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
+
+ dumpStringArray(pw, "provisioningApp", provisioningApp);
+ pw.print("provisioningAppNoUi: ");
+ pw.println(provisioningAppNoUi);
}
public String toString() {
@@ -140,6 +162,8 @@
sj.add(String.format("isDunRequired:%s", isDunRequired));
sj.add(String.format("preferredUpstreamIfaceTypes:%s",
makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
+ sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
+ sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
return String.format("TetheringConfiguration{%s}", sj.toString());
}
@@ -159,6 +183,7 @@
}
private static String makeString(String[] strings) {
+ if (strings == null) return "null";
final StringJoiner sj = new StringJoiner(",", "[", "]");
for (String s : strings) sj.add(s);
return sj.toString();
@@ -195,8 +220,7 @@
}
private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) {
- final int ifaceTypes[] = ctx.getResources().getIntArray(
- com.android.internal.R.array.config_tether_upstream_types);
+ final int ifaceTypes[] = ctx.getResources().getIntArray(config_tether_upstream_types);
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
for (int i : ifaceTypes) {
switch (i) {
@@ -247,14 +271,30 @@
}
private static String[] getDhcpRanges(Context ctx) {
- final String[] fromResource = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_dhcp_range);
+ final String[] fromResource = getResourceStringArray(ctx, config_tether_dhcp_range);
if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
return fromResource;
}
return copy(DHCP_DEFAULT_RANGE);
}
+ private static String getProvisioningAppNoUi(Context ctx) {
+ try {
+ return ctx.getResources().getString(config_mobile_hotspot_provision_app_no_ui);
+ } catch (Resources.NotFoundException e) {
+ return "";
+ }
+ }
+
+ private static String[] getResourceStringArray(Context ctx, int resId) {
+ try {
+ final String[] strArray = ctx.getResources().getStringArray(resId);
+ return (strArray != null) ? strArray : EMPTY_STRING_ARRAY;
+ } catch (Resources.NotFoundException e404) {
+ return EMPTY_STRING_ARRAY;
+ }
+ }
+
private static String[] copy(String[] strarray) {
return Arrays.copyOf(strarray, strarray.length);
}
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index f74daf2..f7439b9 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -42,7 +42,7 @@
*/
public abstract class BrightnessMappingStrategy {
private static final String TAG = "BrightnessMappingStrategy";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private static final float LUX_GRAD_SMOOTHING = 0.25f;
private static final float MAX_GRAD = 1.0f;
@@ -352,9 +352,9 @@
// current^gamma = desired => gamma = log[current](desired)
gamma = MathUtils.log(desiredBrightness) / MathUtils.log(currentBrightness);
// max^-adjustment = gamma => adjustment = -log[max](gamma)
- adjustment = -MathUtils.constrain(
- MathUtils.log(gamma) / MathUtils.log(maxGamma), -1, 1);
+ adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
}
+ adjustment = MathUtils.constrain(adjustment, -1, +1);
if (DEBUG) {
Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" +
MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 4e95bdf..4a1beb1 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -230,10 +230,11 @@
}
List<ActivityManager.RunningTaskInfo> runningTasks = mActivityManager.getTasks(1);
if (!runningTasks.isEmpty()) {
- if (runningTasks.get(0).topActivity.getPackageName()
- != mCurrentClient.getOwnerString()) {
+ final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+ if (!topPackage.contentEquals(mCurrentClient.getOwnerString())) {
mCurrentClient.stop(false /* initiatedByClient */);
- Slog.e(TAG, "Stopping background authentication");
+ Slog.e(TAG, "Stopping background authentication, top: " + topPackage
+ + " currentClient: " + mCurrentClient.getOwnerString());
}
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8e77373..a85960a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -80,6 +80,9 @@
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
+import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_NOTIFICATION_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_DATA_RAPID_NOTIFICATION_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_NOTIFICATION_BOOL;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.internal.util.ArrayUtils.appendInt;
@@ -217,6 +220,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.StatLogger;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -545,6 +549,19 @@
// TODO: migrate notifications to SystemUI
+
+ interface Stats {
+ int UPDATE_NETWORK_ENABLED = 0;
+ int IS_UID_NETWORKING_BLOCKED = 1;
+
+ int COUNT = IS_UID_NETWORKING_BLOCKED + 1;
+ }
+
+ public final StatLogger mStatLogger = new StatLogger(new String[] {
+ "updateNetworkEnabledNL()",
+ "isUidNetworkingBlocked()",
+ });
+
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
INetworkManagementService networkManagement) {
this(context, activityManager, networkManagement, AppGlobals.getPackageManager(),
@@ -1093,8 +1110,10 @@
final long now = mClock.millis();
for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
+ final int subId = findRelevantSubId(policy.template);
+
// ignore policies that aren't relevant to user
- if (!isTemplateRelevant(policy.template)) continue;
+ if (subId == INVALID_SUBSCRIPTION_ID) continue;
if (!policy.hasCycle()) continue;
final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
@@ -1103,28 +1122,43 @@
final long cycleEnd = cycle.second.toInstant().toEpochMilli();
final long totalBytes = getTotalBytes(policy.template, cycleStart, cycleEnd);
- // Notify when data usage is over warning/limit
- if (policy.isOverLimit(totalBytes)) {
- final boolean snoozedThisCycle = policy.lastLimitSnooze >= cycleStart;
- if (snoozedThisCycle) {
- enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes, null);
- } else {
- enqueueNotification(policy, TYPE_LIMIT, totalBytes, null);
- notifyOverLimitNL(policy.template);
+ // Carrier might want to manage notifications themselves
+ final PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+ final boolean notifyWarning = getBooleanDefeatingNullable(config,
+ KEY_DATA_WARNING_NOTIFICATION_BOOL, true);
+ final boolean notifyLimit = getBooleanDefeatingNullable(config,
+ KEY_DATA_LIMIT_NOTIFICATION_BOOL, true);
+ final boolean notifyRapid = getBooleanDefeatingNullable(config,
+ KEY_DATA_RAPID_NOTIFICATION_BOOL, true);
+
+ // Notify when data usage is over warning
+ if (notifyWarning) {
+ if (policy.isOverWarning(totalBytes) && !policy.isOverLimit(totalBytes)) {
+ final boolean snoozedThisCycle = policy.lastWarningSnooze >= cycleStart;
+ if (!snoozedThisCycle) {
+ enqueueNotification(policy, TYPE_WARNING, totalBytes, null);
+ }
}
+ }
- } else {
- notifyUnderLimitNL(policy.template);
-
- final boolean snoozedThisCycle = policy.lastWarningSnooze >= cycleStart;
- if (policy.isOverWarning(totalBytes) && !snoozedThisCycle) {
- enqueueNotification(policy, TYPE_WARNING, totalBytes, null);
+ // Notify when data usage is over limit
+ if (notifyLimit) {
+ if (policy.isOverLimit(totalBytes)) {
+ final boolean snoozedThisCycle = policy.lastLimitSnooze >= cycleStart;
+ if (snoozedThisCycle) {
+ enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes, null);
+ } else {
+ enqueueNotification(policy, TYPE_LIMIT, totalBytes, null);
+ notifyOverLimitNL(policy.template);
+ }
+ } else {
+ notifyUnderLimitNL(policy.template);
}
}
// Warn if average usage over last 4 days is on track to blow pretty
// far past the plan limits.
- if (policy.limitBytes != LIMIT_DISABLED) {
+ if (notifyRapid && policy.limitBytes != LIMIT_DISABLED) {
final long recentDuration = TimeUnit.DAYS.toMillis(4);
final long recentStart = now - recentDuration;
final long recentEnd = now;
@@ -1201,27 +1235,26 @@
* current device state, such as when
* {@link TelephonyManager#getSubscriberId()} matches. This is regardless of
* data connection status.
+ *
+ * @return relevant subId, or {@link #INVALID_SUBSCRIPTION_ID} when no
+ * matching subId found.
*/
- private boolean isTemplateRelevant(NetworkTemplate template) {
- if (template.isMatchRuleMobile()) {
- final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
- final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
+ private int findRelevantSubId(NetworkTemplate template) {
+ final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
+ final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
- // Mobile template is relevant when any active subscriber matches
- final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
- for (int subId : subIds) {
- final String subscriberId = tele.getSubscriberId(subId);
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
- true);
- if (template.matches(probeIdent)) {
- return true;
- }
+ // Mobile template is relevant when any active subscriber matches
+ final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
+ for (int subId : subIds) {
+ final String subscriberId = tele.getSubscriberId(subId);
+ final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
+ true);
+ if (template.matches(probeIdent)) {
+ return subId;
}
- return false;
- } else {
- return true;
}
+ return INVALID_SUBSCRIPTION_ID;
}
/**
@@ -1572,6 +1605,8 @@
// TODO: reset any policy-disabled networks when any policy is removed
// completely, which is currently rare case.
+ final long startTime = mStatLogger.getTime();
+
for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
// shortcut when policy has no limit
@@ -1593,6 +1628,8 @@
setNetworkTemplateEnabled(policy.template, networkEnabled);
}
+
+ mStatLogger.logDurationStat(Stats.UPDATE_NETWORK_ENABLED, startTime);
}
/**
@@ -3086,9 +3123,11 @@
// We can only override when carrier told us about plans
synchronized (mNetworkPoliciesSecondLock) {
- if (ArrayUtils.isEmpty(mSubscriptionPlans.get(subId))) {
+ final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId);
+ if (plan == null
+ || plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) {
throw new IllegalStateException(
- "Must provide SubscriptionPlan information before overriding");
+ "Must provide valid SubscriptionPlan to enable overriding");
}
}
@@ -3280,6 +3319,9 @@
}
fout.decreaseIndent();
+ fout.println();
+ mStatLogger.dump(fout);
+
mLogger.dumpLogs(fout);
}
}
@@ -4639,8 +4681,14 @@
@Override
public boolean isUidNetworkingBlocked(int uid, boolean isNetworkMetered) {
+ final long startTime = mStatLogger.getTime();
+
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- return isUidNetworkingBlockedInternal(uid, isNetworkMetered);
+ final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
+
+ mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
+
+ return ret;
}
private boolean isUidNetworkingBlockedInternal(int uid, boolean isNetworkMetered) {
@@ -4715,11 +4763,17 @@
*/
@Override
public boolean isUidNetworkingBlocked(int uid, String ifname) {
+ final long startTime = mStatLogger.getTime();
+
final boolean isNetworkMetered;
synchronized (mNetworkPoliciesSecondLock) {
isNetworkMetered = mMeteredIfaces.contains(ifname);
}
- return isUidNetworkingBlockedInternal(uid, isNetworkMetered);
+ final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
+
+ mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
+
+ return ret;
}
@Override
@@ -4831,7 +4885,21 @@
@GuardedBy("mNetworkPoliciesSecondLock")
private SubscriptionPlan getPrimarySubscriptionPlanLocked(int subId) {
final SubscriptionPlan[] plans = mSubscriptionPlans.get(subId);
- return ArrayUtils.isEmpty(plans) ? null : plans[0];
+ if (!ArrayUtils.isEmpty(plans)) {
+ for (SubscriptionPlan plan : plans) {
+ if (plan.getCycleRule().isRecurring()) {
+ // Recurring plans will always have an active cycle
+ return plan;
+ } else {
+ // Non-recurring plans need manual test for active cycle
+ final Range<ZonedDateTime> cycle = plan.cycleIterator().next();
+ if (cycle.contains(ZonedDateTime.now(mClock))) {
+ return plan;
+ }
+ }
+ }
+ }
+ return null;
}
/**
@@ -4878,6 +4946,11 @@
return (val != null) ? val : new NetworkState[0];
}
+ private static boolean getBooleanDefeatingNullable(@Nullable PersistableBundle bundle,
+ String key, boolean defaultValue) {
+ return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
+ }
+
private class NotificationId {
private final String mTag;
private final int mId;
diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/services/core/java/com/android/server/net/NetworkStatsObservers.java
index 741c206..d840873 100644
--- a/services/core/java/com/android/server/net/NetworkStatsObservers.java
+++ b/services/core/java/com/android/server/net/NetworkStatsObservers.java
@@ -16,7 +16,7 @@
package com.android.server.net;
-import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.app.usage.NetworkStatsManager.MIN_THRESHOLD_BYTES;
import static com.android.internal.util.Preconditions.checkArgument;
@@ -52,8 +52,6 @@
private static final String TAG = "NetworkStatsObservers";
private static final boolean LOGV = false;
- private static final long MIN_THRESHOLD_BYTES = 2 * MB_IN_BYTES;
-
private static final int MSG_REGISTER = 1;
private static final int MSG_UNREGISTER = 2;
private static final int MSG_UPDATE_STATS = 3;
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 36bc096..b61a27a 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -40,4 +40,6 @@
void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
void onNotificationDirectReplied(String key);
void onNotificationSettingsViewed(String key);
+ void onNotificationSmartRepliesAdded(String key, int replyCount);
+ void onNotificationSmartReplySent(String key, int replyIndex);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7e04d33..7254acf 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -121,6 +121,7 @@
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.IRingtonePlayer;
+import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -395,6 +396,8 @@
private GroupHelper mGroupHelper;
private boolean mIsTelevision;
+ private MetricsLogger mMetricsLogger;
+
private static class Archive {
final int mBufferSize;
final ArrayDeque<StatusBarNotification> mBuffer;
@@ -801,6 +804,18 @@
// Report to usage stats that notification was made visible
if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
reportSeen(r);
+
+ // If the newly visible notification has smart replies
+ // then log that the user has seen them.
+ if (r.getNumSmartRepliesAdded() > 0
+ && !r.hasSeenSmartReplies()) {
+ r.setSeenSmartReplies(true);
+ LogMaker logMaker = r.getLogMaker()
+ .setCategory(MetricsEvent.SMART_REPLY_VISIBLE)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT,
+ r.getNumSmartRepliesAdded());
+ mMetricsLogger.write(logMaker);
+ }
}
r.setVisibility(true, nv.rank);
nv.recycle();
@@ -855,6 +870,31 @@
}
@Override
+ public void onNotificationSmartRepliesAdded(String key, int replyCount) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ r.setNumSmartRepliesAdded(replyCount);
+ }
+ }
+ }
+
+ @Override
+ public void onNotificationSmartReplySent(String key, int replyIndex) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ LogMaker logMaker = r.getLogMaker()
+ .setCategory(MetricsEvent.SMART_REPLY_ACTION)
+ .setSubtype(replyIndex);
+ mMetricsLogger.write(logMaker);
+ // Treat clicking on a smart reply as a user interaction.
+ reportUserInteraction(r);
+ }
+ }
+ }
+
+ @Override
public void onNotificationSettingsViewed(String key) {
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
@@ -1349,6 +1389,7 @@
extractorNames = new String[0];
}
mUsageStats = usageStats;
+ mMetricsLogger = new MetricsLogger();
mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
mConditionProviders = conditionProviders;
mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
@@ -3956,9 +3997,14 @@
+ ", notificationUid=" + notificationUid
+ ", notification=" + notification;
Log.e(TAG, noChannelStr);
- doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
- "Failed to post notification on channel \"" + channelId + "\"\n" +
- "See log for more details");
+ boolean appNotificationsOff = mRankingHelper.getImportance(pkg, notificationUid)
+ == NotificationManager.IMPORTANCE_NONE;
+
+ if (!appNotificationsOff) {
+ doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
+ "Failed to post notification on channel \"" + channelId + "\"\n" +
+ "See log for more details");
+ }
return;
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index c887085..9bd3e52 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -149,6 +149,8 @@
private final NotificationStats mStats;
private int mUserSentiment;
private boolean mIsInterruptive;
+ private int mNumberOfSmartRepliesAdded;
+ private boolean mHasSeenSmartReplies;
@VisibleForTesting
public NotificationRecord(Context context, StatusBarNotification sbn,
@@ -962,6 +964,22 @@
mStats.setViewedSettings();
}
+ public void setNumSmartRepliesAdded(int noReplies) {
+ mNumberOfSmartRepliesAdded = noReplies;
+ }
+
+ public int getNumSmartRepliesAdded() {
+ return mNumberOfSmartRepliesAdded;
+ }
+
+ public boolean hasSeenSmartReplies() {
+ return mHasSeenSmartReplies;
+ }
+
+ public void setSeenSmartReplies(boolean hasSeenSmartReplies) {
+ mHasSeenSmartReplies = hasSeenSmartReplies;
+ }
+
public Set<Uri> getNotificationUris() {
Notification notification = getNotification();
Set<Uri> uris = new ArraySet<>();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5b45cbe..2265450 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1523,7 +1523,6 @@
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,
params.traceMethod, params.traceCookie);
}
- return;
}
mPendingInstalls.clear();
} else {
@@ -5305,7 +5304,7 @@
synchronized (mPackages) {
final String[] packageNames = getPackagesForUid(uid);
final PackageParser.Package pkg = (packageNames != null && packageNames.length > 0)
- ? mSettings.getPackageLPr(packageNames[0]).getPackage()
+ ? mPackages.get(packageNames[0])
: null;
return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid());
}
@@ -8071,6 +8070,7 @@
callingUid = mIsolatedOwners.get(callingUid);
}
final PackageSetting ps = mSettings.mPackages.get(packageName);
+ PackageParser.Package pkg = mPackages.get(packageName);
final boolean returnAllowed =
ps != null
&& (isCallerSameApp(packageName, callingUid)
@@ -8141,7 +8141,7 @@
}
private boolean isCallerSameApp(String packageName, int uid) {
- PackageParser.Package pkg = mSettings.getPackageLPr(packageName).getPackage();
+ PackageParser.Package pkg = mPackages.get(packageName);
return pkg != null
&& UserHandle.getAppId(uid) == pkg.applicationInfo.uid;
}
@@ -10182,20 +10182,10 @@
// The signature has changed, but this package is in the system
// image... let's recover!
pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
- // However... if this package is part of a shared user, but it
- // doesn't match the signature of the shared user, let's fail.
- // What this means is that you can't change the signatures
- // associated with an overall shared user, which doesn't seem all
- // that unreasonable.
+ // If the system app is part of a shared user we allow that shared user to change
+ // signatures as well in part as part of an OTA.
if (signatureCheckPs.sharedUser != null) {
- if (compareSignatures(
- signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
- pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
- throw new PackageManagerException(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- "Signature mismatch for shared user: "
- + pkgSetting.sharedUser);
- }
+ signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
}
// File a report about this.
String msg = "System package " + pkg.packageName
@@ -14021,7 +14011,7 @@
"setPackagesSuspended for user " + userId);
if (callingUid != Process.ROOT_UID &&
!UserHandle.isSameApp(getPackageUid(callingPackage, 0, userId), callingUid)) {
- throw new IllegalArgumentException("callingPackage " + callingPackage + " does not"
+ throw new IllegalArgumentException("CallingPackage " + callingPackage + " does not"
+ " belong to calling app id " + UserHandle.getAppId(callingUid));
}
@@ -14045,20 +14035,18 @@
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null
|| filterAppAccessLPr(pkgSetting, callingUid, userId)) {
- Slog.w(TAG, "Could not find package setting for package \"" + packageName
- + "\". Skipping suspending/un-suspending.");
+ Slog.w(TAG, "Could not find package setting for package: " + packageName
+ + ". Skipping suspending/un-suspending.");
unactionedPackages.add(packageName);
continue;
}
- if (pkgSetting.getSuspended(userId) != suspended) {
- if (!canSuspendPackageForUserLocked(packageName, userId)) {
- unactionedPackages.add(packageName);
- continue;
- }
- pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
- launcherExtras, userId);
- changedPackagesList.add(packageName);
+ if (!canSuspendPackageForUserLocked(packageName, userId)) {
+ unactionedPackages.add(packageName);
+ continue;
}
+ pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
+ launcherExtras, userId);
+ changedPackagesList.add(packageName);
}
}
} finally {
@@ -14073,7 +14061,6 @@
scheduleWritePackageRestrictionsLocked(userId);
}
}
-
return unactionedPackages.toArray(new String[unactionedPackages.size()]);
}
@@ -14081,7 +14068,8 @@
public PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
if (getPackageUid(packageName, 0, userId) != callingUid) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
+ throw new SecurityException("Calling package " + packageName
+ + " does not belong to calling uid " + callingUid);
}
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -14096,25 +14084,6 @@
}
}
- @Override
- public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras,
- int userId) {
- final int callingUid = Binder.getCallingUid();
- mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
- synchronized (mPackages) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
- throw new IllegalArgumentException("Unknown target package: " + packageName);
- }
- final PackageUserState packageUserState = ps.readUserState(userId);
- if (packageUserState.suspended) {
- packageUserState.suspendedAppExtras = appExtras;
- sendMyPackageSuspendedOrUnsuspended(new String[] {packageName}, true, appExtras,
- userId);
- }
- }
- }
-
private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
PersistableBundle appExtras, int userId) {
final String action;
@@ -14157,9 +14126,6 @@
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"isPackageSuspendedForUser for user " + userId);
- if (getPackageUid(packageName, 0, userId) != callingUid) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
- }
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
@@ -14169,18 +14135,26 @@
}
}
- void onSuspendingPackageRemoved(String packageName, int userId) {
- final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds()
- : new int[] {userId};
- synchronized (mPackages) {
- for (PackageSetting ps : mSettings.mPackages.values()) {
- for (int user : userIds) {
- final PackageUserState pus = ps.readUserState(user);
+ void onSuspendingPackageRemoved(String packageName, int removedForUser) {
+ final int[] userIds = (removedForUser == UserHandle.USER_ALL) ? sUserManager.getUserIds()
+ : new int[] {removedForUser};
+ for (int userId : userIds) {
+ List<String> affectedPackages = new ArrayList<>();
+ synchronized (mPackages) {
+ for (PackageSetting ps : mSettings.mPackages.values()) {
+ final PackageUserState pus = ps.readUserState(userId);
if (pus.suspended && packageName.equals(pus.suspendingPackage)) {
- ps.setSuspended(false, null, null, null, null, user);
+ ps.setSuspended(false, null, null, null, null, userId);
+ affectedPackages.add(ps.name);
}
}
}
+ if (!affectedPackages.isEmpty()) {
+ final String[] packageArray = affectedPackages.toArray(
+ new String[affectedPackages.size()]);
+ sendMyPackageSuspendedOrUnsuspended(packageArray, false, null, userId);
+ sendPackagesSuspendedForUser(packageArray, userId, false, null);
+ }
}
}
@@ -23624,13 +23598,6 @@
}
@Override
- public Object getPackageSetting(String packageName) {
- synchronized (mPackages) {
- return mSettings.getPackageLPr(packageName);
- }
- }
-
- @Override
public PackageList getPackageList(PackageListObserver observer) {
synchronized (mPackages) {
final int N = mPackages.size();
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index fd4c5e9..138594c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -20,8 +20,6 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-
import android.content.pm.ApplicationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index eeaa333..8c7871f 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -21,6 +21,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -164,12 +165,13 @@
ShortcutService s, String packageName, @UserIdInt int packageUserId) {
final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, packageUserId);
// retrieve the newest sigs
- Signature[][] signingHistory = pi.signingCertificateHistory;
- if (signingHistory == null || signingHistory.length == 0) {
+ SigningInfo signingInfo = pi.signingInfo;
+ if (signingInfo == null) {
Slog.e(TAG, "Can't get signatures: package=" + packageName);
return null;
}
- Signature[] signatures = signingHistory[signingHistory.length - 1];
+ // TODO (b/73988180) use entire signing history in case of rollbacks
+ Signature[] signatures = signingInfo.getApkContentsSigners();
final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.getLongVersionCode(),
pi.lastUpdateTime, BackupUtils.hashSignatureArray(signatures), /* shadow=*/ false);
@@ -192,13 +194,14 @@
return;
}
// retrieve the newest sigs
- Signature[][] signingHistory = pi.signingCertificateHistory;
- if (signingHistory == null || signingHistory.length == 0) {
+ SigningInfo signingInfo = pi.signingInfo;
+ if (signingInfo == null) {
Slog.w(TAG, "Not refreshing signature for " + pkg.getPackageName()
- + " since it appears to have no signature history.");
+ + " since it appears to have no signing info.");
return;
}
- Signature[] signatures = signingHistory[signingHistory.length - 1];
+ // TODO (b/73988180) use entire signing history in case of rollbacks
+ Signature[] signatures = signingInfo.getApkContentsSigners();
mSigHashes = BackupUtils.hashSignatureArray(signatures);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 15b4617..599e5a5 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -48,7 +48,6 @@
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
-import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
@@ -100,7 +99,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
-import com.android.server.StatLogger;
+import com.android.internal.util.StatLogger;
import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
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 065133b..f5b52fc 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -212,11 +212,12 @@
return PackageManager.PERMISSION_DENIED;
}
- final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(pkgName);
- if (ps != null && ps.getPackage() != null) {
- if (mPackageManagerInt.filterAppAccess(ps.getPackage(), callingUid, userId)) {
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName);
+ if (pkg != null && pkg.mExtras != null) {
+ if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
return PackageManager.PERMISSION_DENIED;
}
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
final boolean instantApp = ps.getInstantApp(userId);
final PermissionsState permissionsState = ps.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
diff --git a/services/core/java/com/android/server/slice/DirtyTracker.java b/services/core/java/com/android/server/slice/DirtyTracker.java
new file mode 100644
index 0000000..4288edc
--- /dev/null
+++ b/services/core/java/com/android/server/slice/DirtyTracker.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.server.slice;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * A parent object that cares when a Persistable changes and will schedule a serialization
+ * in response to the onPersistableDirty callback.
+ */
+public interface DirtyTracker {
+ void onPersistableDirty(Persistable obj);
+
+ /**
+ * An object that can be written to XML.
+ */
+ interface Persistable {
+ String getFileName();
+ void writeTo(XmlSerializer out) throws IOException;
+ }
+}
diff --git a/services/core/java/com/android/server/slice/SliceClientPermissions.java b/services/core/java/com/android/server/slice/SliceClientPermissions.java
new file mode 100644
index 0000000..e461e0d
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SliceClientPermissions.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT 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.slice;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.server.slice.DirtyTracker.Persistable;
+import com.android.server.slice.SlicePermissionManager.PkgUser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class SliceClientPermissions implements DirtyTracker, Persistable {
+
+ private static final String TAG = "SliceClientPermissions";
+
+ static final String TAG_CLIENT = "client";
+ private static final String TAG_AUTHORITY = "authority";
+ private static final String TAG_PATH = "path";
+ private static final String NAMESPACE = null;
+
+ private static final String ATTR_PKG = "pkg";
+ private static final String ATTR_AUTHORITY = "authority";
+ private static final String ATTR_FULL_ACCESS = "fullAccess";
+
+ private final PkgUser mPkg;
+ // Keyed off (authority, userId) rather than the standard (pkg, userId)
+ private final ArrayMap<PkgUser, SliceAuthority> mAuths = new ArrayMap<>();
+ private final DirtyTracker mTracker;
+ private boolean mHasFullAccess;
+
+ public SliceClientPermissions(@NonNull PkgUser pkg, @NonNull DirtyTracker tracker) {
+ mPkg = pkg;
+ mTracker = tracker;
+ }
+
+ public PkgUser getPkg() {
+ return mPkg;
+ }
+
+ public synchronized Collection<SliceAuthority> getAuthorities() {
+ return new ArrayList<>(mAuths.values());
+ }
+
+ public synchronized SliceAuthority getOrCreateAuthority(PkgUser authority, PkgUser provider) {
+ SliceAuthority ret = mAuths.get(authority);
+ if (ret == null) {
+ ret = new SliceAuthority(authority.getPkg(), provider, this);
+ mAuths.put(authority, ret);
+ onPersistableDirty(ret);
+ }
+ return ret;
+ }
+
+ public synchronized SliceAuthority getAuthority(PkgUser authority) {
+ return mAuths.get(authority);
+ }
+
+ public boolean hasFullAccess() {
+ return mHasFullAccess;
+ }
+
+ public void setHasFullAccess(boolean hasFullAccess) {
+ if (mHasFullAccess == hasFullAccess) return;
+ mHasFullAccess = hasFullAccess;
+ mTracker.onPersistableDirty(this);
+ }
+
+ public void removeAuthority(String authority, int userId) {
+ if (mAuths.remove(new PkgUser(authority, userId)) != null) {
+ mTracker.onPersistableDirty(this);
+ }
+ }
+
+ public synchronized boolean hasPermission(Uri uri, int userId) {
+ if (!Objects.equals(ContentResolver.SCHEME_CONTENT, uri.getScheme())) return false;
+ SliceAuthority authority = getAuthority(new PkgUser(uri.getAuthority(), userId));
+ return authority != null && authority.hasPermission(uri.getPathSegments());
+ }
+
+ public void grantUri(Uri uri, PkgUser providerPkg) {
+ SliceAuthority authority = getOrCreateAuthority(
+ new PkgUser(uri.getAuthority(), providerPkg.getUserId()),
+ providerPkg);
+ authority.addPath(uri.getPathSegments());
+ }
+
+ public void revokeUri(Uri uri, PkgUser providerPkg) {
+ SliceAuthority authority = getOrCreateAuthority(
+ new PkgUser(uri.getAuthority(), providerPkg.getUserId()),
+ providerPkg);
+ authority.removePath(uri.getPathSegments());
+ }
+
+ public void clear() {
+ if (!mHasFullAccess && mAuths.isEmpty()) return;
+ mHasFullAccess = false;
+ mAuths.clear();
+ onPersistableDirty(this);
+ }
+
+ @Override
+ public void onPersistableDirty(Persistable obj) {
+ mTracker.onPersistableDirty(this);
+ }
+
+ @Override
+ public String getFileName() {
+ return getFileName(mPkg);
+ }
+
+ public synchronized void writeTo(XmlSerializer out) throws IOException {
+ out.startTag(NAMESPACE, TAG_CLIENT);
+ out.attribute(NAMESPACE, ATTR_PKG, mPkg.toString());
+ out.attribute(NAMESPACE, ATTR_FULL_ACCESS, mHasFullAccess ? "1" : "0");
+
+ final int N = mAuths.size();
+ for (int i = 0; i < N; i++) {
+ out.startTag(NAMESPACE, TAG_AUTHORITY);
+ out.attribute(NAMESPACE, ATTR_AUTHORITY, mAuths.valueAt(i).mAuthority);
+ out.attribute(NAMESPACE, ATTR_PKG, mAuths.valueAt(i).mPkg.toString());
+
+ mAuths.valueAt(i).writeTo(out);
+
+ out.endTag(NAMESPACE, TAG_AUTHORITY);
+ }
+
+ out.endTag(NAMESPACE, TAG_CLIENT);
+ }
+
+ public static SliceClientPermissions createFrom(XmlPullParser parser, DirtyTracker tracker)
+ throws XmlPullParserException, IOException {
+ // Get to the beginning of the provider.
+ while (parser.getEventType() != XmlPullParser.START_TAG
+ || !TAG_CLIENT.equals(parser.getName())) {
+ parser.next();
+ }
+ int depth = parser.getDepth();
+ PkgUser pkgUser = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG));
+ SliceClientPermissions provider = new SliceClientPermissions(pkgUser, tracker);
+ String fullAccess = parser.getAttributeValue(NAMESPACE, ATTR_FULL_ACCESS);
+ if (fullAccess == null) {
+ fullAccess = "0";
+ }
+ provider.mHasFullAccess = Integer.parseInt(fullAccess) != 0;
+ parser.next();
+
+ while (parser.getDepth() > depth) {
+ if (parser.getEventType() == XmlPullParser.START_TAG
+ && TAG_AUTHORITY.equals(parser.getName())) {
+ try {
+ PkgUser pkg = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG));
+ SliceAuthority authority = new SliceAuthority(
+ parser.getAttributeValue(NAMESPACE, ATTR_AUTHORITY), pkg, provider);
+ authority.readFrom(parser);
+ provider.mAuths.put(new PkgUser(authority.getAuthority(), pkg.getUserId()),
+ authority);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Couldn't read PkgUser", e);
+ }
+ }
+
+ parser.next();
+ }
+ return provider;
+ }
+
+ public static String getFileName(PkgUser pkg) {
+ return String.format("client_%s", pkg.toString());
+ }
+
+ public static class SliceAuthority implements Persistable {
+ public static final String DELIMITER = "/";
+ private final String mAuthority;
+ private final DirtyTracker mTracker;
+ private final PkgUser mPkg;
+ private final ArraySet<String[]> mPaths = new ArraySet<>();
+
+ public SliceAuthority(String authority, PkgUser pkg, DirtyTracker tracker) {
+ mAuthority = authority;
+ mPkg = pkg;
+ mTracker = tracker;
+ }
+
+ public String getAuthority() {
+ return mAuthority;
+ }
+
+ public PkgUser getPkg() {
+ return mPkg;
+ }
+
+ void addPath(List<String> path) {
+ String[] pathSegs = path.toArray(new String[path.size()]);
+ for (int i = mPaths.size() - 1; i >= 0; i--) {
+ String[] existing = mPaths.valueAt(i);
+ if (isPathPrefixMatch(existing, pathSegs)) {
+ // Nothing to add here.
+ return;
+ }
+ if (isPathPrefixMatch(pathSegs, existing)) {
+ mPaths.removeAt(i);
+ }
+ }
+ mPaths.add(pathSegs);
+ mTracker.onPersistableDirty(this);
+ }
+
+ void removePath(List<String> path) {
+ boolean changed = false;
+ String[] pathSegs = path.toArray(new String[path.size()]);
+ for (int i = mPaths.size() - 1; i >= 0; i--) {
+ String[] existing = mPaths.valueAt(i);
+ if (isPathPrefixMatch(pathSegs, existing)) {
+ changed = true;
+ mPaths.removeAt(i);
+ }
+ }
+ if (changed) {
+ mTracker.onPersistableDirty(this);
+ }
+ }
+
+ public synchronized Collection<String[]> getPaths() {
+ return new ArraySet<>(mPaths);
+ }
+
+ public boolean hasPermission(List<String> path) {
+ for (String[] p : mPaths) {
+ if (isPathPrefixMatch(p, path.toArray(new String[path.size()]))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isPathPrefixMatch(String[] prefix, String[] path) {
+ final int prefixSize = prefix.length;
+ if (path.length < prefixSize) return false;
+
+ for (int i = 0; i < prefixSize; i++) {
+ if (!Objects.equals(path[i], prefix[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public String getFileName() {
+ return null;
+ }
+
+ public synchronized void writeTo(XmlSerializer out) throws IOException {
+ final int N = mPaths.size();
+ for (int i = 0; i < N; i++) {
+ out.startTag(NAMESPACE, TAG_PATH);
+ out.text(encodeSegments(mPaths.valueAt(i)));
+ out.endTag(NAMESPACE, TAG_PATH);
+ }
+ }
+
+ public synchronized void readFrom(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ parser.next();
+ int depth = parser.getDepth();
+ while (parser.getDepth() >= depth) {
+ if (parser.getEventType() == XmlPullParser.START_TAG
+ && TAG_PATH.equals(parser.getName())) {
+ mPaths.add(decodeSegments(parser.nextText()));
+ }
+ parser.next();
+ }
+ }
+
+ private String encodeSegments(String[] s) {
+ String[] out = new String[s.length];
+ for (int i = 0; i < s.length; i++) {
+ out[i] = Uri.encode(s[i]);
+ }
+ return TextUtils.join(DELIMITER, out);
+ }
+
+ private String[] decodeSegments(String s) {
+ String[] sets = s.split(DELIMITER, -1);
+ for (int i = 0; i < sets.length; i++) {
+ sets[i] = Uri.decode(sets[i]);
+ }
+ return sets;
+ }
+
+ /**
+ * Only for testing, no deep equality of these are done normally.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!getClass().equals(obj != null ? obj.getClass() : null)) return false;
+ SliceAuthority other = (SliceAuthority) obj;
+ if (mPaths.size() != other.mPaths.size()) return false;
+ ArrayList<String[]> p1 = new ArrayList<>(mPaths);
+ ArrayList<String[]> p2 = new ArrayList<>(other.mPaths);
+ p1.sort(Comparator.comparing(o -> TextUtils.join(",", o)));
+ p2.sort(Comparator.comparing(o -> TextUtils.join(",", o)));
+ for (int i = 0; i < p1.size(); i++) {
+ String[] a1 = p1.get(i);
+ String[] a2 = p2.get(i);
+ if (a1.length != a2.length) return false;
+ for (int j = 0; j < a1.length; j++) {
+ if (!Objects.equals(a1[j], a2[j])) return false;
+ }
+ }
+ return Objects.equals(mAuthority, other.mAuthority)
+ && Objects.equals(mPkg, other.mPkg);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("(%s, %s: %s)", mAuthority, mPkg.toString(), pathToString(mPaths));
+ }
+
+ private String pathToString(ArraySet<String[]> paths) {
+ return TextUtils.join(", ", paths.stream().map(s -> TextUtils.join("/", s))
+ .collect(Collectors.toList()));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index fd0b6f1..b7b9612 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -31,14 +31,15 @@
import android.app.ContentProviderHolder;
import android.app.IActivityManager;
import android.app.slice.ISliceManager;
-import android.app.slice.SliceManager;
import android.app.slice.SliceSpec;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.net.Uri;
@@ -51,7 +52,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml.Encoding;
@@ -72,7 +72,6 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@@ -91,13 +90,9 @@
@GuardedBy("mLock")
private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
- @GuardedBy("mLock")
- private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
private final Handler mHandler;
- @GuardedBy("mSliceAccessFile")
- private final AtomicFile mSliceAccessFile;
- @GuardedBy("mAccessList")
- private final SliceFullAccessList mAccessList;
+
+ private final SlicePermissionManager mPermissions;
private final UsageStatsManagerInternal mAppUsageStats;
public SliceManagerService(Context context) {
@@ -113,24 +108,9 @@
mAssistUtils = new AssistUtils(context);
mHandler = new Handler(looper);
- final File systemDir = new File(Environment.getDataDirectory(), "system");
- mSliceAccessFile = new AtomicFile(new File(systemDir, "slice_access.xml"));
- mAccessList = new SliceFullAccessList(mContext);
mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
- synchronized (mSliceAccessFile) {
- if (!mSliceAccessFile.exists()) return;
- try {
- InputStream input = mSliceAccessFile.openRead();
- XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
- parser.setInput(input, Encoding.UTF_8.name());
- synchronized (mAccessList) {
- mAccessList.readXml(parser);
- }
- } catch (IOException | XmlPullParserException e) {
- Slog.d(TAG, "Can't read slice access file", e);
- }
- }
+ mPermissions = new SlicePermissionManager(mContext, looper);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
@@ -211,26 +191,58 @@
}
@Override
+ public void grantSlicePermission(String pkg, String toPkg, Uri uri) throws RemoteException {
+ verifyCaller(pkg);
+ int user = Binder.getCallingUserHandle().getIdentifier();
+ enforceOwner(pkg, uri, user);
+ mPermissions.grantSliceAccess(toPkg, user, pkg, user, uri);
+ }
+
+ @Override
+ public void revokeSlicePermission(String pkg, String toPkg, Uri uri) throws RemoteException {
+ verifyCaller(pkg);
+ int user = Binder.getCallingUserHandle().getIdentifier();
+ enforceOwner(pkg, uri, user);
+ mPermissions.revokeSliceAccess(toPkg, user, pkg, user, uri);
+ }
+
+ @Override
public int checkSlicePermission(Uri uri, String pkg, int pid, int uid,
- String[] autoGrantPermissions) throws RemoteException {
+ String[] autoGrantPermissions) {
+ int userId = UserHandle.getUserId(uid);
+ if (pkg == null) {
+ for (String p : mContext.getPackageManager().getPackagesForUid(uid)) {
+ if (checkSlicePermission(uri, p, pid, uid, autoGrantPermissions)
+ == PERMISSION_GRANTED) {
+ return PERMISSION_GRANTED;
+ }
+ }
+ return PERMISSION_DENIED;
+ }
+ if (hasFullSliceAccess(pkg, userId)) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ if (mPermissions.hasPermission(pkg, userId, uri)) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ if (autoGrantPermissions != null) {
+ // Need to own the Uri to call in with permissions to grant.
+ enforceOwner(pkg, uri, userId);
+ for (String perm : autoGrantPermissions) {
+ if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
+ int providerUser = ContentProvider.getUserIdFromUri(uri, userId);
+ String providerPkg = getProviderPkg(uri, providerUser);
+ mPermissions.grantSliceAccess(pkg, userId, providerPkg, providerUser, uri);
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ }
+ // Fallback to allowing uri permissions through.
if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
== PERMISSION_GRANTED) {
- return SliceManager.PERMISSION_GRANTED;
+ return PackageManager.PERMISSION_GRANTED;
}
- if (hasFullSliceAccess(pkg, UserHandle.getUserId(uid))) {
- return SliceManager.PERMISSION_GRANTED;
- }
- for (String perm : autoGrantPermissions) {
- if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
- return SliceManager.PERMISSION_USER_GRANTED;
- }
- }
- synchronized (mLock) {
- if (mUserGrants.contains(new SliceGrant(uri, pkg, UserHandle.getUserId(uid)))) {
- return SliceManager.PERMISSION_USER_GRANTED;
- }
- }
- return SliceManager.PERMISSION_DENIED;
+ return PackageManager.PERMISSION_DENIED;
}
@Override
@@ -238,16 +250,17 @@
verifyCaller(callingPkg);
getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
"Slice granting requires MANAGE_SLICE_PERMISSIONS");
+ int userId = Binder.getCallingUserHandle().getIdentifier();
if (allSlices) {
- synchronized (mAccessList) {
- mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
- }
- mHandler.post(mSaveAccessList);
+ mPermissions.grantFullAccess(pkg, userId);
} else {
- synchronized (mLock) {
- mUserGrants.add(new SliceGrant(uri, pkg,
- Binder.getCallingUserHandle().getIdentifier()));
- }
+ // When granting, grant to all slices in the provider.
+ Uri grantUri = uri.buildUpon()
+ .path("")
+ .build();
+ int providerUser = ContentProvider.getUserIdFromUri(grantUri, userId);
+ String providerPkg = getProviderPkg(grantUri, providerUser);
+ mPermissions.grantSliceAccess(pkg, userId, providerPkg, providerUser, grantUri);
}
long ident = Binder.clearCallingIdentity();
try {
@@ -268,19 +281,17 @@
Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
return null;
}
- synchronized(mSliceAccessFile) {
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
- out.setOutput(baos, Encoding.UTF_8.name());
- synchronized (mAccessList) {
- mAccessList.writeXml(out, user);
- }
- out.flush();
- return baos.toByteArray();
- } catch (IOException | XmlPullParserException e) {
- Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
- }
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
+ out.setOutput(baos, Encoding.UTF_8.name());
+
+ mPermissions.writeBackup(out);
+
+ out.flush();
+ return baos.toByteArray();
+ } catch (IOException | XmlPullParserException e) {
+ Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
}
return null;
}
@@ -299,27 +310,21 @@
Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
return;
}
- synchronized(mSliceAccessFile) {
- final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
- try {
- XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
- parser.setInput(bais, Encoding.UTF_8.name());
- synchronized (mAccessList) {
- mAccessList.readXml(parser);
- }
- mHandler.post(mSaveAccessList);
- } catch (NumberFormatException | XmlPullParserException | IOException e) {
- Slog.w(TAG, "applyRestore: error reading payload", e);
- }
+ final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
+ try {
+ XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+ parser.setInput(bais, Encoding.UTF_8.name());
+ mPermissions.readRestore(parser);
+ } catch (NumberFormatException | XmlPullParserException | IOException e) {
+ Slog.w(TAG, "applyRestore: error reading payload", e);
}
}
/// ----- internal code -----
- private void removeFullAccess(String pkg, int userId) {
- synchronized (mAccessList) {
- mAccessList.removeGrant(pkg, userId);
+ private void enforceOwner(String pkg, Uri uri, int user) {
+ if (!Objects.equals(getProviderPkg(uri, user), pkg) || pkg == null) {
+ throw new SecurityException("Caller must own " + uri);
}
- mHandler.post(mSaveAccessList);
}
protected void removePinnedSlice(Uri uri) {
@@ -368,19 +373,7 @@
}
protected int checkAccess(String pkg, Uri uri, int uid, int pid) {
- int user = UserHandle.getUserId(uid);
- // Check for default launcher/assistant.
- if (!hasFullSliceAccess(pkg, user)) {
- // Also allow things with uri access.
- if (getContext().checkUriPermission(uri, pid, uid,
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) {
- // Last fallback (if the calling app owns the authority, then it can have access).
- if (!Objects.equals(getProviderPkg(uri, user), pkg)) {
- return PERMISSION_DENIED;
- }
- }
- }
- return PERMISSION_GRANTED;
+ return checkSlicePermission(uri, pkg, uid, pid, null);
}
private String getProviderPkg(Uri uri, int user) {
@@ -425,15 +418,11 @@
private void enforceAccess(String pkg, Uri uri) throws RemoteException {
if (checkAccess(pkg, uri, Binder.getCallingUid(), Binder.getCallingPid())
!= PERMISSION_GRANTED) {
- throw new SecurityException("Access to slice " + uri + " is required");
- }
- enforceCrossUser(pkg, uri);
- }
-
- private void enforceFullAccess(String pkg, String name, Uri uri) {
- int user = Binder.getCallingUserHandle().getIdentifier();
- if (!hasFullSliceAccess(pkg, user)) {
- throw new SecurityException(String.format("Call %s requires full slice access", name));
+ int userId = ContentProvider.getUserIdFromUri(uri,
+ Binder.getCallingUserHandle().getIdentifier());
+ if (!Objects.equals(pkg, getProviderPkg(uri, userId))) {
+ throw new SecurityException("Access to slice " + uri + " is required");
+ }
}
enforceCrossUser(pkg, uri);
}
@@ -513,9 +502,7 @@
}
private boolean isGrantedFullAccess(String pkg, int userId) {
- synchronized (mAccessList) {
- return mAccessList.hasFullAccess(pkg, userId);
- }
+ return mPermissions.hasFullAccess(pkg, userId);
}
private static ServiceThread createHandler() {
@@ -525,34 +512,6 @@
return handlerThread;
}
- private final Runnable mSaveAccessList = new Runnable() {
- @Override
- public void run() {
- synchronized (mSliceAccessFile) {
- final FileOutputStream stream;
- try {
- stream = mSliceAccessFile.startWrite();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save access file", e);
- return;
- }
-
- try {
- XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
- out.setOutput(stream, Encoding.UTF_8.name());
- synchronized (mAccessList) {
- mAccessList.writeXml(out, UserHandle.USER_ALL);
- }
- out.flush();
- mSliceAccessFile.finishWrite(stream);
- } catch (IOException | XmlPullParserException e) {
- Slog.w(TAG, "Failed to save access file, restoring backup", e);
- mSliceAccessFile.failWrite(stream);
- }
- }
- }
- };
-
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -572,11 +531,11 @@
final boolean replacing =
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
if (!replacing) {
- removeFullAccess(pkg, userId);
+ mPermissions.removePkg(pkg, userId);
}
break;
case Intent.ACTION_PACKAGE_DATA_CLEARED:
- removeFullAccess(pkg, userId);
+ mPermissions.removePkg(pkg, userId);
break;
}
}
diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java
new file mode 100644
index 0000000..d25ec89
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT 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.slice;
+
+import android.content.ContentProvider;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml.Encoding;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+import com.android.server.slice.SliceProviderPermissions.SliceAuthority;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Objects;
+
+public class SlicePermissionManager implements DirtyTracker {
+
+ private static final String TAG = "SlicePermissionManager";
+
+ /**
+ * The amount of time we'll cache a SliceProviderPermissions or SliceClientPermissions
+ * in case they are used again.
+ */
+ private static final long PERMISSION_CACHE_PERIOD = 5 * DateUtils.MINUTE_IN_MILLIS;
+
+ /**
+ * The amount of time we delay flushing out permission changes to disk because they usually
+ * come in short bursts.
+ */
+ private static final long WRITE_GRACE_PERIOD = 500;
+
+ private static final String SLICE_DIR = "slice";
+
+ // If/when this bumps again we'll need to write it out in the disk somewhere.
+ // Currently we don't have a central file for this in version 2 and there is no
+ // reason to add one until we actually have incompatible version bumps.
+ // This does however block us from reading backups from P-DP1 which may contain
+ // a very different XML format for perms.
+ static final int DB_VERSION = 2;
+
+ private static final String TAG_LIST = "slice-access-list";
+ private final String ATT_VERSION = "version";
+
+ private final File mSliceDir;
+ private final Context mContext;
+ private final Handler mHandler;
+ private final ArrayMap<PkgUser, SliceProviderPermissions> mCachedProviders = new ArrayMap<>();
+ private final ArrayMap<PkgUser, SliceClientPermissions> mCachedClients = new ArrayMap<>();
+ private final ArraySet<Persistable> mDirty = new ArraySet<>();
+
+ @VisibleForTesting
+ SlicePermissionManager(Context context, Looper looper, File sliceDir) {
+ mContext = context;
+ mHandler = new H(looper);
+ mSliceDir = sliceDir;
+ }
+
+ public SlicePermissionManager(Context context, Looper looper) {
+ this(context, looper, new File(Environment.getDataDirectory(), "system/" + SLICE_DIR));
+ }
+
+ public void grantFullAccess(String pkg, int userId) {
+ PkgUser pkgUser = new PkgUser(pkg, userId);
+ SliceClientPermissions client = getClient(pkgUser);
+ client.setHasFullAccess(true);
+ }
+
+ public void grantSliceAccess(String pkg, int userId, String providerPkg, int providerUser,
+ Uri uri) {
+ PkgUser pkgUser = new PkgUser(pkg, userId);
+ PkgUser providerPkgUser = new PkgUser(providerPkg, providerUser);
+
+ SliceClientPermissions client = getClient(pkgUser);
+ client.grantUri(uri, providerPkgUser);
+
+ SliceProviderPermissions provider = getProvider(providerPkgUser);
+ provider.getOrCreateAuthority(ContentProvider.getUriWithoutUserId(uri).getAuthority())
+ .addPkg(pkgUser);
+ }
+
+ public void revokeSliceAccess(String pkg, int userId, String providerPkg, int providerUser,
+ Uri uri) {
+ PkgUser pkgUser = new PkgUser(pkg, userId);
+ PkgUser providerPkgUser = new PkgUser(providerPkg, providerUser);
+
+ SliceClientPermissions client = getClient(pkgUser);
+ client.revokeUri(uri, providerPkgUser);
+ }
+
+ public void removePkg(String pkg, int userId) {
+ PkgUser pkgUser = new PkgUser(pkg, userId);
+ SliceProviderPermissions provider = getProvider(pkgUser);
+
+ for (SliceAuthority authority : provider.getAuthorities()) {
+ for (PkgUser p : authority.getPkgs()) {
+ getClient(p).removeAuthority(authority.getAuthority(), userId);
+ }
+ }
+ SliceClientPermissions client = getClient(pkgUser);
+ client.clear();
+ mHandler.obtainMessage(H.MSG_REMOVE, pkgUser);
+ }
+
+ public boolean hasFullAccess(String pkg, int userId) {
+ PkgUser pkgUser = new PkgUser(pkg, userId);
+ return getClient(pkgUser).hasFullAccess();
+ }
+
+ public boolean hasPermission(String pkg, int userId, Uri uri) {
+ PkgUser pkgUser = new PkgUser(pkg, userId);
+ SliceClientPermissions client = getClient(pkgUser);
+ int providerUserId = ContentProvider.getUserIdFromUri(uri, userId);
+ return client.hasFullAccess()
+ || client.hasPermission(ContentProvider.getUriWithoutUserId(uri), providerUserId);
+ }
+
+ @Override
+ public void onPersistableDirty(Persistable obj) {
+ mHandler.removeMessages(H.MSG_PERSIST);
+ mHandler.obtainMessage(H.MSG_ADD_DIRTY, obj).sendToTarget();
+ mHandler.sendEmptyMessageDelayed(H.MSG_PERSIST, WRITE_GRACE_PERIOD);
+ }
+
+ public void writeBackup(XmlSerializer out) throws IOException, XmlPullParserException {
+ synchronized (this) {
+ out.startTag(null, TAG_LIST);
+ out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
+
+ // Don't do anything with changes from the backup, because there shouldn't be any.
+ DirtyTracker tracker = obj -> { };
+ if (mHandler.hasMessages(H.MSG_PERSIST)) {
+ mHandler.removeMessages(H.MSG_PERSIST);
+ handlePersist();
+ }
+ for (String file : new File(mSliceDir.getAbsolutePath()).list()) {
+ if (file.isEmpty()) continue;
+ try (ParserHolder parser = getParser(file)) {
+ Persistable p;
+ while (parser.parser.getEventType() != XmlPullParser.START_TAG) {
+ parser.parser.next();
+ }
+ if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) {
+ p = SliceClientPermissions.createFrom(parser.parser, tracker);
+ } else {
+ p = SliceProviderPermissions.createFrom(parser.parser, tracker);
+ }
+ p.writeTo(out);
+ }
+ }
+
+ out.endTag(null, TAG_LIST);
+ }
+ }
+
+ public void readRestore(XmlPullParser parser) throws IOException, XmlPullParserException {
+ synchronized (this) {
+ while ((parser.getEventType() != XmlPullParser.START_TAG
+ || !TAG_LIST.equals(parser.getName()))
+ && parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ parser.next();
+ }
+ int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0);
+ if (xmlVersion < DB_VERSION) {
+ // No conversion support right now.
+ return;
+ }
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if (parser.getEventType() == XmlPullParser.START_TAG) {
+ if (SliceClientPermissions.TAG_CLIENT.equals(parser.getName())) {
+ SliceClientPermissions client = SliceClientPermissions.createFrom(parser,
+ this);
+ synchronized (mCachedClients) {
+ mCachedClients.put(client.getPkg(), client);
+ }
+ onPersistableDirty(client);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(H.MSG_CLEAR_CLIENT, client.getPkg()),
+ PERMISSION_CACHE_PERIOD);
+ } else if (SliceProviderPermissions.TAG_PROVIDER.equals(parser.getName())) {
+ SliceProviderPermissions provider = SliceProviderPermissions.createFrom(
+ parser, this);
+ synchronized (mCachedProviders) {
+ mCachedProviders.put(provider.getPkg(), provider);
+ }
+ onPersistableDirty(provider);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(H.MSG_CLEAR_PROVIDER, provider.getPkg()),
+ PERMISSION_CACHE_PERIOD);
+ } else {
+ parser.next();
+ }
+ } else {
+ parser.next();
+ }
+ }
+ }
+ }
+
+ private SliceClientPermissions getClient(PkgUser pkgUser) {
+ SliceClientPermissions client;
+ synchronized (mCachedClients) {
+ client = mCachedClients.get(pkgUser);
+ }
+ if (client == null) {
+ try (ParserHolder parser = getParser(SliceClientPermissions.getFileName(pkgUser))) {
+ client = SliceClientPermissions.createFrom(parser.parser, this);
+ synchronized (mCachedClients) {
+ mCachedClients.put(pkgUser, client);
+ }
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(H.MSG_CLEAR_CLIENT, pkgUser),
+ PERMISSION_CACHE_PERIOD);
+ return client;
+ } catch (FileNotFoundException e) {
+ // No client exists yet.
+ } catch (IOException e) {
+ Log.e(TAG, "Can't read client", e);
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "Can't read client", e);
+ }
+ // Can't read or no permissions exist, create a clean object.
+ client = new SliceClientPermissions(pkgUser, this);
+ }
+ return client;
+ }
+
+ private SliceProviderPermissions getProvider(PkgUser pkgUser) {
+ SliceProviderPermissions provider;
+ synchronized (mCachedProviders) {
+ provider = mCachedProviders.get(pkgUser);
+ }
+ if (provider == null) {
+ try (ParserHolder parser = getParser(SliceProviderPermissions.getFileName(pkgUser))) {
+ provider = SliceProviderPermissions.createFrom(parser.parser, this);
+ synchronized (mCachedProviders) {
+ mCachedProviders.put(pkgUser, provider);
+ }
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(H.MSG_CLEAR_PROVIDER, pkgUser),
+ PERMISSION_CACHE_PERIOD);
+ return provider;
+ } catch (FileNotFoundException e) {
+ // No provider exists yet.
+ } catch (IOException e) {
+ Log.e(TAG, "Can't read provider", e);
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "Can't read provider", e);
+ }
+ // Can't read or no permissions exist, create a clean object.
+ provider = new SliceProviderPermissions(pkgUser, this);
+ }
+ return provider;
+ }
+
+ private ParserHolder getParser(String fileName)
+ throws FileNotFoundException, XmlPullParserException {
+ AtomicFile file = getFile(fileName);
+ ParserHolder holder = new ParserHolder();
+ holder.input = file.openRead();
+ holder.parser = XmlPullParserFactory.newInstance().newPullParser();
+ holder.parser.setInput(holder.input, Encoding.UTF_8.name());
+ return holder;
+ }
+
+ private AtomicFile getFile(String fileName) {
+ if (!mSliceDir.exists()) {
+ mSliceDir.mkdir();
+ }
+ return new AtomicFile(new File(mSliceDir, fileName));
+ }
+
+ private void handlePersist() {
+ synchronized (this) {
+ for (Persistable persistable : mDirty) {
+ AtomicFile file = getFile(persistable.getFileName());
+ final FileOutputStream stream;
+ try {
+ stream = file.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save access file", e);
+ return;
+ }
+
+ try {
+ XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
+ out.setOutput(stream, Encoding.UTF_8.name());
+
+ persistable.writeTo(out);
+
+ out.flush();
+ file.finishWrite(stream);
+ } catch (IOException | XmlPullParserException e) {
+ Slog.w(TAG, "Failed to save access file, restoring backup", e);
+ file.failWrite(stream);
+ }
+ }
+ mDirty.clear();
+ }
+ }
+
+ private void handleRemove(PkgUser pkgUser) {
+ getFile(SliceClientPermissions.getFileName(pkgUser)).delete();
+ getFile(SliceProviderPermissions.getFileName(pkgUser)).delete();
+ mDirty.remove(mCachedClients.remove(pkgUser));
+ mDirty.remove(mCachedProviders.remove(pkgUser));
+ }
+
+ private final class H extends Handler {
+ private static final int MSG_ADD_DIRTY = 1;
+ private static final int MSG_PERSIST = 2;
+ private static final int MSG_REMOVE = 3;
+ private static final int MSG_CLEAR_CLIENT = 4;
+ private static final int MSG_CLEAR_PROVIDER = 5;
+
+ public H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ADD_DIRTY:
+ mDirty.add((Persistable) msg.obj);
+ break;
+ case MSG_PERSIST:
+ handlePersist();
+ break;
+ case MSG_REMOVE:
+ handleRemove((PkgUser) msg.obj);
+ break;
+ case MSG_CLEAR_CLIENT:
+ synchronized (mCachedClients) {
+ mCachedClients.remove(msg.obj);
+ }
+ break;
+ case MSG_CLEAR_PROVIDER:
+ synchronized (mCachedProviders) {
+ mCachedProviders.remove(msg.obj);
+ }
+ break;
+ }
+ }
+ }
+
+ public static class PkgUser {
+ private static final String SEPARATOR = "@";
+ private static final String FORMAT = "%s" + SEPARATOR + "%d";
+ private final String mPkg;
+ private final int mUserId;
+
+ public PkgUser(String pkg, int userId) {
+ mPkg = pkg;
+ mUserId = userId;
+ }
+
+ public PkgUser(String pkgUserStr) throws IllegalArgumentException {
+ try {
+ String[] vals = pkgUserStr.split(SEPARATOR, 2);
+ mPkg = vals[0];
+ mUserId = Integer.parseInt(vals[1]);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public String getPkg() {
+ return mPkg;
+ }
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ @Override
+ public int hashCode() {
+ return mPkg.hashCode() + mUserId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!getClass().equals(obj != null ? obj.getClass() : null)) return false;
+ PkgUser other = (PkgUser) obj;
+ return Objects.equals(other.mPkg, mPkg) && other.mUserId == mUserId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(FORMAT, mPkg, mUserId);
+ }
+ }
+
+ private class ParserHolder implements AutoCloseable {
+
+ private InputStream input;
+ private XmlPullParser parser;
+
+ @Override
+ public void close() throws IOException {
+ input.close();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/slice/SliceProviderPermissions.java b/services/core/java/com/android/server/slice/SliceProviderPermissions.java
new file mode 100644
index 0000000..6e602d5
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SliceProviderPermissions.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT 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.slice;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.server.slice.DirtyTracker.Persistable;
+import com.android.server.slice.SlicePermissionManager.PkgUser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Objects;
+
+public class SliceProviderPermissions implements DirtyTracker, Persistable {
+
+ private static final String TAG = "SliceProviderPermissions";
+
+ static final String TAG_PROVIDER = "provider";
+ private static final String TAG_AUTHORITY = "authority";
+ private static final String TAG_PKG = "pkg";
+ private static final String NAMESPACE = null;
+
+ private static final String ATTR_PKG = "pkg";
+ private static final String ATTR_AUTHORITY = "authority";
+
+ private final PkgUser mPkg;
+ private final ArrayMap<String, SliceAuthority> mAuths = new ArrayMap<>();
+ private final DirtyTracker mTracker;
+
+ public SliceProviderPermissions(@NonNull PkgUser pkg, @NonNull DirtyTracker tracker) {
+ mPkg = pkg;
+ mTracker = tracker;
+ }
+
+ public PkgUser getPkg() {
+ return mPkg;
+ }
+
+ public synchronized Collection<SliceAuthority> getAuthorities() {
+ return new ArrayList<>(mAuths.values());
+ }
+
+ public synchronized SliceAuthority getOrCreateAuthority(String authority) {
+ SliceAuthority ret = mAuths.get(authority);
+ if (ret == null) {
+ ret = new SliceAuthority(authority, this);
+ mAuths.put(authority, ret);
+ onPersistableDirty(ret);
+ }
+ return ret;
+ }
+
+ @Override
+ public void onPersistableDirty(Persistable obj) {
+ mTracker.onPersistableDirty(this);
+ }
+
+ @Override
+ public String getFileName() {
+ return getFileName(mPkg);
+ }
+
+ public synchronized void writeTo(XmlSerializer out) throws IOException {
+ out.startTag(NAMESPACE, TAG_PROVIDER);
+ out.attribute(NAMESPACE, ATTR_PKG, mPkg.toString());
+
+ final int N = mAuths.size();
+ for (int i = 0; i < N; i++) {
+ out.startTag(NAMESPACE, TAG_AUTHORITY);
+ out.attribute(NAMESPACE, ATTR_AUTHORITY, mAuths.valueAt(i).mAuthority);
+
+ mAuths.valueAt(i).writeTo(out);
+
+ out.endTag(NAMESPACE, TAG_AUTHORITY);
+ }
+
+ out.endTag(NAMESPACE, TAG_PROVIDER);
+ }
+
+ public static SliceProviderPermissions createFrom(XmlPullParser parser, DirtyTracker tracker)
+ throws XmlPullParserException, IOException {
+ // Get to the beginning of the provider.
+ while (parser.getEventType() != XmlPullParser.START_TAG
+ || !TAG_PROVIDER.equals(parser.getName())) {
+ parser.next();
+ }
+ int depth = parser.getDepth();
+ PkgUser pkgUser = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG));
+ SliceProviderPermissions provider = new SliceProviderPermissions(pkgUser, tracker);
+ parser.next();
+
+ while (parser.getDepth() > depth) {
+ if (parser.getEventType() == XmlPullParser.START_TAG
+ && TAG_AUTHORITY.equals(parser.getName())) {
+ try {
+ SliceAuthority authority = new SliceAuthority(
+ parser.getAttributeValue(NAMESPACE, ATTR_AUTHORITY), provider);
+ authority.readFrom(parser);
+ provider.mAuths.put(authority.getAuthority(), authority);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Couldn't read PkgUser", e);
+ }
+ }
+
+ parser.next();
+ }
+ return provider;
+ }
+
+ public static String getFileName(PkgUser pkg) {
+ return String.format("provider_%s", pkg.toString());
+ }
+
+ public static class SliceAuthority implements Persistable {
+ private final String mAuthority;
+ private final DirtyTracker mTracker;
+ private final ArraySet<PkgUser> mPkgs = new ArraySet<>();
+
+ public SliceAuthority(String authority, DirtyTracker tracker) {
+ mAuthority = authority;
+ mTracker = tracker;
+ }
+
+ public String getAuthority() {
+ return mAuthority;
+ }
+
+ public synchronized void addPkg(PkgUser pkg) {
+ if (mPkgs.add(pkg)) {
+ mTracker.onPersistableDirty(this);
+ }
+ }
+
+ public synchronized void removePkg(PkgUser pkg) {
+ if (mPkgs.remove(pkg)) {
+ mTracker.onPersistableDirty(this);
+ }
+ }
+
+ public synchronized Collection<PkgUser> getPkgs() {
+ return new ArraySet<>(mPkgs);
+ }
+
+ @Override
+ public String getFileName() {
+ return null;
+ }
+
+ public synchronized void writeTo(XmlSerializer out) throws IOException {
+ final int N = mPkgs.size();
+ for (int i = 0; i < N; i++) {
+ out.startTag(NAMESPACE, TAG_PKG);
+ out.text(mPkgs.valueAt(i).toString());
+ out.endTag(NAMESPACE, TAG_PKG);
+ }
+ }
+
+ public synchronized void readFrom(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ parser.next();
+ int depth = parser.getDepth();
+ while (parser.getDepth() >= depth) {
+ if (parser.getEventType() == XmlPullParser.START_TAG
+ && TAG_PKG.equals(parser.getName())) {
+ mPkgs.add(new PkgUser(parser.nextText()));
+ }
+ parser.next();
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!getClass().equals(obj != null ? obj.getClass() : null)) return false;
+ SliceAuthority other = (SliceAuthority) obj;
+ return Objects.equals(mAuthority, other.mAuthority)
+ && Objects.equals(mPkgs, other.mPkgs);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("(%s: %s)", mAuthority, mPkgs.toString());
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 2db8039..36fa868 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1096,6 +1096,30 @@
}
@Override
+ public void onNotificationSmartRepliesAdded(String key, int replyCount)
+ throws RemoteException {
+ enforceStatusBarService();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.onNotificationSmartRepliesAdded(key, replyCount);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void onNotificationSmartReplySent(String key, int replyIndex)
+ throws RemoteException {
+ enforceStatusBarService();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void onNotificationSettingsViewed(String key) throws RemoteException {
enforceStatusBarService();
long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 641a1ba..608d0aa 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1102,35 +1102,37 @@
}
}
- // Account for the space this window takes if the window
- // is not an accessibility overlay which does not change
- // the reported windows.
if (windowState.mAttrs.type !=
WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+
+ // Account for the space this window takes if the window
+ // is not an accessibility overlay which does not change
+ // the reported windows.
unaccountedSpace.op(boundsInScreen, unaccountedSpace,
Region.Op.REVERSE_DIFFERENCE);
- }
- // If a window is modal it prevents other windows from being touched
- if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
- // Account for all space in the task, whether the windows in it are
- // touchable or not. The modal window blocks all touches from the task's
- // area.
- unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
- Region.Op.REVERSE_DIFFERENCE);
+ // If a window is modal it prevents other windows from being touched
+ if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+ // Account for all space in the task, whether the windows in it are
+ // touchable or not. The modal window blocks all touches from the task's
+ // area.
+ unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
- if (task != null) {
- // If the window is associated with a particular task, we can skip the
- // rest of the windows for that task.
- skipRemainingWindowsForTasks.add(task.mTaskId);
- continue;
- } else {
- // If the window is not associated with a particular task, then it is
- // globally modal. In this case we can skip all remaining windows.
- break;
+ if (task != null) {
+ // If the window is associated with a particular task, we can skip the
+ // rest of the windows for that task.
+ skipRemainingWindowsForTasks.add(task.mTaskId);
+ continue;
+ } else {
+ // If the window is not associated with a particular task, then it is
+ // globally modal. In this case we can skip all remaining windows.
+ break;
+ }
}
}
+
// We figured out what is touchable for the entire screen - done.
if (unaccountedSpace.isEmpty()) {
break;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 9a4db65..5676f58 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1312,7 +1312,8 @@
if (prevWinMode != WINDOWING_MODE_UNDEFINED && winMode == WINDOWING_MODE_PINNED) {
// Entering PiP from fullscreen, reset the snap fraction
mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
- } else if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED) {
+ } else if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED
+ && !isHidden()) {
// Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds
// for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index bae93f3..1ee642a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -17,19 +17,16 @@
package com.android.server.wm;
import static android.app.ActivityManagerInternal.APP_TRANSITION_RECENTS_ANIM;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
-import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
import android.annotation.IntDef;
import android.app.ActivityManager.TaskSnapshot;
@@ -41,24 +38,22 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;import android.util.proto.ProtoOutputStream;
+import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-
import com.android.internal.annotations.VisibleForTesting;
-import com.google.android.collect.Sets;
-
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.InsetUtils;
-
+import com.google.android.collect.Sets;
import java.io.PrintWriter;
import java.util.ArrayList;
+
/**
* Controls a single instance of the remote driven recents animation. In particular, this allows
* the calling SystemUI to animate the visible task windows as a part of the transition. The remote
@@ -67,8 +62,7 @@
* app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
*/
public class RecentsAnimationController implements DeathRecipient {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentsAnimationController" : TAG_WM;
- private static final boolean DEBUG = false;
+ private static final String TAG = RecentsAnimationController.class.getSimpleName();
private static final long FAILSAFE_DELAY = 1000;
public static final int REORDER_KEEP_IN_PLACE = 0;
@@ -88,7 +82,7 @@
private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
private final int mDisplayId;
private final Runnable mFailsafeRunnable = () -> {
- cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
};
// The recents component app token that is shown behind the visibile tasks
@@ -124,7 +118,8 @@
@Override
public TaskSnapshot screenshotTask(int taskId) {
- if (DEBUG) Log.d(TAG, "screenshotTask(" + taskId + "): mCanceled=" + mCanceled);
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "screenshotTask(" + taskId + "):"
+ + " mCanceled=" + mCanceled);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mService.getWindowManagerLock()) {
@@ -153,7 +148,8 @@
@Override
public void finish(boolean moveHomeToTop) {
- if (DEBUG) Log.d(TAG, "finish(" + moveHomeToTop + "): mCanceled=" + mCanceled);
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "finish(" + moveHomeToTop + "):"
+ + " mCanceled=" + mCanceled);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mService.getWindowManagerLock()) {
@@ -190,8 +186,8 @@
@Override
public void setInputConsumerEnabled(boolean enabled) {
- if (DEBUG) Log.d(TAG, "setInputConsumerEnabled(" + enabled + "): mCanceled="
- + mCanceled);
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setInputConsumerEnabled(" + enabled + "):"
+ + " mCanceled=" + mCanceled);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mService.getWindowManagerLock()) {
@@ -264,14 +260,14 @@
// Skip the animation if there is nothing to animate
if (mPendingAnimations.isEmpty()) {
- cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks");
return;
}
try {
linkToDeathOfRunner();
} catch (RemoteException e) {
- cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath");
return;
}
@@ -279,7 +275,8 @@
final AppWindowToken recentsComponentAppToken = dc.getStack(WINDOWING_MODE_UNDEFINED,
targetActivityType).getTopChild().getTopFullscreenAppToken();
if (recentsComponentAppToken != null) {
- if (DEBUG) Log.d(TAG, "setHomeApp(" + recentsComponentAppToken.getName() + ")");
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setHomeApp("
+ + recentsComponentAppToken.getName() + ")");
mTargetAppToken = recentsComponentAppToken;
if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
@@ -295,7 +292,7 @@
@VisibleForTesting
AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
- if (DEBUG) Log.d(TAG, "addAnimation(" + task.getName() + ")");
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "addAnimation(" + task.getName() + ")");
// TODO: Refactor this to use the task's animator
final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
mService);
@@ -309,14 +306,15 @@
@VisibleForTesting
void removeAnimation(TaskAnimationAdapter taskAdapter) {
- if (DEBUG) Log.d(TAG, "removeAnimation(" + taskAdapter.mTask.getName() + ")");
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "removeAnimation("
+ + taskAdapter.mTask.mTaskId + ")");
taskAdapter.mTask.setCanAffectSystemUiFlags(true);
taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter);
mPendingAnimations.remove(taskAdapter);
}
void startAnimation() {
- if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
+ " mCanceled=" + mCanceled);
if (!mPendingStart || mCanceled) {
// Skip starting if we've already started or canceled the animation
@@ -336,7 +334,7 @@
// Skip the animation if there is nothing to animate
if (appAnimations.isEmpty()) {
- cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows");
return;
}
@@ -354,6 +352,13 @@
: null;
mRunner.onAnimationStart_New(mController, appTargets, contentInsets,
minimizedHomeBounds);
+ if (DEBUG_RECENTS_ANIMATIONS) {
+ Slog.d(TAG, "startAnimation(): Notify animation start:");
+ for (int i = 0; i < mPendingAnimations.size(); i++) {
+ final Task task = mPendingAnimations.get(i).mTask;
+ Slog.d(TAG, "\t" + task.mTaskId);
+ }
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start recents animation", e);
}
@@ -363,8 +368,8 @@
reasons).sendToTarget();
}
- void cancelAnimation(@ReorderMode int reorderMode) {
- if (DEBUG) Log.d(TAG, "cancelAnimation()");
+ void cancelAnimation(@ReorderMode int reorderMode, String reason) {
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation()");
synchronized (mService.getWindowManagerLock()) {
if (mCanceled) {
// We've already canceled the animation
@@ -385,8 +390,9 @@
}
void cleanupAnimation(@ReorderMode int reorderMode) {
- if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations="
- + mPendingAnimations.size());
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG,
+ "cleanupAnimation(): Notify animation finished mPendingAnimations="
+ + mPendingAnimations.size() + " reorderMode=" + reorderMode);
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
@@ -421,7 +427,7 @@
@Override
public void binderDied() {
- cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
}
void checkAnimationReady(WallpaperController wallpaperController) {
@@ -550,7 +556,7 @@
@Override
public void onAnimationCancelled(SurfaceControl animationLeash) {
- cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled");
}
@Override
@@ -572,6 +578,10 @@
} else {
pw.print(prefix); pw.println("Target: null");
}
+ pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
+ pw.println("mPosition=" + mPosition);
+ pw.println("mBounds=" + mBounds);
+ pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
}
@Override
@@ -588,6 +598,10 @@
final String innerPrefix = prefix + " ";
pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
+ pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
+ pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
+ pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
pw.print(innerPrefix); pw.println("mTargetAppToken=" + mTargetAppToken);
+ pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper());
}
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 16f4cd0..1b06b2f 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -19,6 +19,7 @@
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -49,7 +50,9 @@
* Helper class to run app animations in a remote process.
*/
class RemoteAnimationController implements DeathRecipient {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
+ private static final String TAG = TAG_WITH_CLASS_NAME
+ || (DEBUG_REMOTE_ANIMATIONS && !DEBUG_APP_TRANSITIONS)
+ ? "RemoteAnimationController" : TAG_WM;
private static final long TIMEOUT_MS = 2000;
private final WindowManagerService mService;
@@ -57,7 +60,7 @@
private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
private final Rect mTmpRect = new Rect();
private final Handler mHandler;
- private final Runnable mTimeoutRunnable = this::cancelAnimation;
+ private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
private FinishedCallback mFinishedCallback;
private boolean mCanceled;
@@ -80,6 +83,7 @@
*/
AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
Rect stackBounds) {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token=" + appWindowToken);
final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
appWindowToken, position, stackBounds);
mPendingAnimations.add(adapter);
@@ -90,7 +94,10 @@
* Called when the transition is ready to be started, and all leashes have been set up.
*/
void goodToGo() {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo()");
if (mPendingAnimations.isEmpty() || mCanceled) {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished before good to go, canceled="
+ + mCanceled + " mPendingAnimations=" + mPendingAnimations.size());
onAnimationFinished();
return;
}
@@ -102,6 +109,7 @@
final RemoteAnimationTarget[] animations = createAnimations();
if (animations.length == 0) {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): No apps to animate");
onAnimationFinished();
return;
}
@@ -113,14 +121,20 @@
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
}
+ if (DEBUG_REMOTE_ANIMATIONS) {
+ Slog.d(TAG, "startAnimation(): Notify animation start:");
+ for (int i = 0; i < mPendingAnimations.size(); i++) {
+ Slog.d(TAG, "\t" + mPendingAnimations.get(i).mAppWindowToken);
+ }
+ } else if (DEBUG_APP_TRANSITIONS) {
+ writeStartDebugStatement();
+ }
});
sendRunningRemoteAnimation(true);
- if (DEBUG_APP_TRANSITIONS) {
- writeStartDebugStatement();
- }
}
- private void cancelAnimation() {
+ private void cancelAnimation(String reason) {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
synchronized (mService.getWindowManagerLock()) {
if (mCanceled) {
return;
@@ -143,14 +157,16 @@
}
private RemoteAnimationTarget[] createAnimations() {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimations()");
final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final RemoteAnimationAdapterWrapper wrapper = mPendingAnimations.get(i);
- final RemoteAnimationTarget target =
- mPendingAnimations.get(i).createRemoteAppAnimation();
+ final RemoteAnimationTarget target = wrapper.createRemoteAppAnimation();
if (target != null) {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrapper.mAppWindowToken);
targets.add(target);
} else {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token=" + wrapper.mAppWindowToken);
// We can't really start an animation but we still need to make sure to finish the
// pending animation that was started by SurfaceAnimator
@@ -164,22 +180,29 @@
}
private void onAnimationFinished() {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): mPendingAnimations="
+ + mPendingAnimations.size());
mHandler.removeCallbacks(mTimeoutRunnable);
synchronized (mService.mWindowMap) {
unlinkToDeathOfRunner();
releaseFinishedCallback();
mService.openSurfaceTransaction();
try {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): Notify animation finished:");
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\t" + adapter.mAppWindowToken);
}
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to finish remote animation", e);
+ throw e;
} finally {
mService.closeSurfaceTransaction("RemoteAnimationController#finished");
}
}
sendRunningRemoteAnimation(false);
- if (DEBUG_APP_TRANSITIONS) Slog.i(TAG, "Finishing remote animation");
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.i(TAG, "Finishing remote animation");
}
private void invokeAnimationCancelled() {
@@ -221,7 +244,7 @@
@Override
public void binderDied() {
- cancelAnimation();
+ cancelAnimation("binderDied");
}
private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
@@ -234,6 +257,7 @@
@Override
public void onAnimationFinished() throws RemoteException {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-onAnimationFinished(): mOuter=" + mOuter);
final long token = Binder.clearCallingIdentity();
try {
if (mOuter != null) {
@@ -253,6 +277,7 @@
* to prevent memory leak.
*/
void release() {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-release(): mOuter=" + mOuter);
mOuter = null;
}
};
@@ -316,6 +341,7 @@
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
// Restore z-layering, position and stack crop until client has a chance to modify it.
t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 662d51d..c808c91 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -45,6 +45,7 @@
import android.util.Slog;
import android.view.Display;
import android.view.DisplayCutout;
+import android.view.DisplayCutout.ParcelableWrapper;
import android.view.IWindow;
import android.view.IWindowId;
import android.view.IWindowSession;
@@ -218,7 +219,7 @@
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */,
- null /* cutout */, null /* outInputChannel */);
+ new ParcelableWrapper() /* cutout */, null /* outInputChannel */);
}
@Override
@@ -233,16 +234,16 @@
@Override
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
- int requestedWidth, int requestedHeight, int viewFlags,
- int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
- Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
- DisplayCutout.ParcelableWrapper cutout,
- MergedConfiguration mergedConfiguration, Surface outSurface) {
+ int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
+ Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
+ Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
+ DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
+ Surface outSurface) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
int res = mService.relayoutWindow(this, window, seq, attrs,
- requestedWidth, requestedHeight, viewFlags, flags,
+ requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
outStableInsets, outsets, outBackdropFrame, cutout,
mergedConfiguration, outSurface);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 597b39e..5721bd8 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -215,8 +215,8 @@
currentOrientation);
window.setOuter(snapshotSurface);
try {
- session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
- tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
+ session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
+ tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
tmpCutout, tmpMergedConfiguration, surface);
} catch (RemoteException e) {
// Local call.
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 2175c6b..ae9e802 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -37,6 +37,7 @@
import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
import static com.android.server.wm.StackProto.ADJUST_IME_AMOUNT;
+import static com.android.server.wm.StackProto.ANIMATING_BOUNDS;
import static com.android.server.wm.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
import static com.android.server.wm.StackProto.BOUNDS;
import static com.android.server.wm.StackProto.DEFER_REMOVAL;
@@ -827,6 +828,14 @@
}
}
+ if (inSplitScreenSecondaryWindowingMode()) {
+ // When the stack is resized due to entering split screen secondary, offset the
+ // windows to compensate for the new stack position.
+ forAllWindows(w -> {
+ w.mWinAnimator.setOffsetPositionForStackResize(true);
+ }, true);
+ }
+
updateDisplayInfo(bounds);
updateSurfaceBounds();
}
@@ -1363,6 +1372,7 @@
proto.write(ADJUST_IME_AMOUNT, mAdjustImeAmount);
proto.write(ADJUST_DIVIDER_AMOUNT, mAdjustDividerAmount);
mAdjustedBounds.writeToProto(proto, ADJUSTED_BOUNDS);
+ proto.write(ANIMATING_BOUNDS, mBoundsAnimating);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7eaca5d..c63da77 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -620,7 +620,7 @@
// If there was a recents animation in progress, cancel that animation
if (mService.getRecentsAnimationController() != null) {
mService.getRecentsAnimationController().cancelAnimation(
- REORDER_MOVE_TO_ORIGINAL_POSITION);
+ REORDER_MOVE_TO_ORIGINAL_POSITION, "wallpaperDrawPendingTimeout");
}
return true;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 9d9805a..990eb97 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -74,6 +74,9 @@
static final boolean SHOW_STACK_CRAWLS = false;
static final boolean DEBUG_WINDOW_CROP = false;
static final boolean DEBUG_UNKNOWN_APP_VISIBILITY = false;
+ // TODO (b/73188263): Reset debugging flags
+ static final boolean DEBUG_RECENTS_ANIMATIONS = true;
+ static final boolean DEBUG_REMOTE_ANIMATIONS = DEBUG_APP_TRANSITIONS || true;
static final String TAG_KEEP_SCREEN_ON = "DebugKeepScreenOn";
static final boolean DEBUG_KEEP_SCREEN_ON = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2b5620c..407312a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1835,10 +1835,9 @@
}
}
- public int relayoutWindow(Session session, IWindow client, int seq,
- LayoutParams attrs, int requestedWidth,
- int requestedHeight, int viewVisibility, int flags,
- Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
+ public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
+ int requestedWidth, int requestedHeight, int viewVisibility, int flags,
+ long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
Surface outSurface) {
@@ -1865,6 +1864,7 @@
win.setRequestedSize(requestedWidth, requestedHeight);
}
+ win.setFrameNumber(frameNumber);
int attrChanges = 0;
int flagChanges = 0;
if (attrs != null) {
@@ -2729,13 +2729,19 @@
}
}
- public void cancelRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
+ /**
+ * Cancels any running recents animation. The caller should NOT hold the WM lock while calling
+ * this method, as it can call back into AM, and locking will be done in the animation
+ * controller itself.
+ */
+ public void cancelRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode,
+ String reason) {
// Note: Do not hold the WM lock, this will lock appropriately in the call which also
// calls through to AM/RecentsAnimation.onAnimationFinished()
if (mRecentsAnimationController != null) {
// This call will call through to cleanupAnimation() below after the animation is
// canceled
- mRecentsAnimationController.cancelAnimation(reorderMode);
+ mRecentsAnimationController.cancelAnimation(reorderMode, reason);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c5269e1..da5bc73 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -636,6 +636,11 @@
private PowerManagerWrapper mPowerManagerWrapper;
/**
+ * A frame number in which changes requested in this layout will be rendered.
+ */
+ private long mFrameNumber = -1;
+
+ /**
* Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
* of z-order and 1 otherwise.
*/
@@ -4655,7 +4660,7 @@
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
t.deferTransactionUntil(mSurfaceControl,
mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
- mAttrs.frameNumber);
+ getFrameNumber());
}
}
}
@@ -4771,6 +4776,14 @@
return mService.mInputMethodTarget == this;
}
+ long getFrameNumber() {
+ return mFrameNumber;
+ }
+
+ void setFrameNumber(long frameNumber) {
+ mFrameNumber = frameNumber;
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e92d460..ab5e24a 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -67,7 +67,6 @@
import com.android.server.policy.WindowManagerPolicy;
-import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
@@ -217,6 +216,12 @@
int mXOffset = 0;
int mYOffset = 0;
+ /**
+ * A flag to determine if the WSA needs to offset its position to compensate for the stack's
+ * position update before the WSA surface has resized.
+ */
+ private boolean mOffsetPositionForStackResize;
+
private final Rect mTmpSize = new Rect();
private final SurfaceControl.Transaction mReparentTransaction = new SurfaceControl.Transaction();
@@ -230,6 +235,8 @@
// once per animation.
boolean mPipAnimationStarted = false;
+ private final Point mTmpPos = new Point();
+
WindowStateAnimator(final WindowState win) {
final WindowManagerService service = win.mService;
@@ -498,6 +505,8 @@
mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
attrs.getTitle().toString(), width, height, format, flags, this,
windowType, ownerUid);
+
+ setOffsetPositionForStackResize(false);
mSurfaceFormat = format;
w.setHasSurface(true);
@@ -859,7 +868,8 @@
// However, this would be unsafe, as the client may be in the middle
// of producing a frame at the old size, having just completed layout
// to find the surface size changed underneath it.
- if (!w.mRelayoutCalled || w.mInRelayout) {
+ final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
+ if (relayout) {
mSurfaceResized = mSurfaceController.setSizeInTransaction(
mTmpSize.width(), mTmpSize.height(), recoveringMemory);
} else {
@@ -996,7 +1006,38 @@
mPipAnimationStarted = false;
if (!w.mSeamlesslyRotated) {
- mSurfaceController.setPositionInTransaction(mXOffset, mYOffset, recoveringMemory);
+ // Used to offset the WSA when stack position changes before a resize.
+ int xOffset = mXOffset;
+ int yOffset = mYOffset;
+ if (mOffsetPositionForStackResize) {
+ if (relayout) {
+ // Once a relayout is called, reset the offset back to 0 and defer
+ // setting it until a new frame with the updated size. This ensures that
+ // the WS position is reset (so the stack position is shown) at the same
+ // time that the buffer size changes.
+ setOffsetPositionForStackResize(false);
+ mSurfaceController.deferTransactionUntil(mSurfaceController.getHandle(),
+ mWin.getFrameNumber());
+ } else {
+ final TaskStack stack = mWin.getStack();
+ mTmpPos.x = 0;
+ mTmpPos.y = 0;
+ if (stack != null) {
+ stack.getRelativePosition(mTmpPos);
+ }
+
+ xOffset = -mTmpPos.x;
+ yOffset = -mTmpPos.y;
+
+ // Crop also needs to be extended so the bottom isn't cut off when the WSA
+ // position is moved.
+ if (clipRect != null) {
+ clipRect.right += mTmpPos.x;
+ clipRect.bottom += mTmpPos.y;
+ }
+ }
+ }
+ mSurfaceController.setPositionInTransaction(xOffset, yOffset, recoveringMemory);
}
}
@@ -1499,4 +1540,8 @@
int getLayer() {
return mLastLayer;
}
+
+ void setOffsetPositionForStackResize(boolean offsetPositionForStackResize) {
+ mOffsetPositionForStackResize = offsetPositionForStackResize;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 82d2f3c..e07b89f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -220,7 +220,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
-import com.android.server.StatLogger;
+import com.android.internal.util.StatLogger;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index cecc94b..b4dc941 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -1714,6 +1714,8 @@
}
private void expectNetworkState(boolean roaming) throws Exception {
+ when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
+ .thenReturn(CarrierConfigManager.getDefaultConfig());
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
new NetworkState(buildNetworkInfo(),
buildLinkProperties(TEST_IFACE),
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 9663a1b..08b8af2 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -23,6 +23,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -287,4 +290,43 @@
// Verify that the stack was removed.
assertEquals(originalStackCount, defaultDisplay.getChildCount());
}
+
+ @Test
+ public void testFocusability() throws Exception {
+ final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(stack).build();
+
+ // Under split screen primary we should be focusable when not minimized
+ mService.mStackSupervisor.setDockedStackMinimized(false);
+ assertTrue(stack.isFocusable());
+ assertTrue(activity.isFocusable());
+
+ // Under split screen primary we should not be focusable when minimized
+ mService.mStackSupervisor.setDockedStackMinimized(true);
+ assertFalse(stack.isFocusable());
+ assertFalse(activity.isFocusable());
+
+ final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(pinnedStack).build();
+
+ // We should not be focusable when in pinned mode
+ assertFalse(pinnedStack.isFocusable());
+ assertFalse(pinnedActivity.isFocusable());
+
+ // Add flag forcing focusability.
+ pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
+
+ // We should not be focusable when in pinned mode
+ assertTrue(pinnedStack.isFocusable());
+ assertTrue(pinnedActivity.isFocusable());
+
+ // Without the overridding activity, stack should not be focusable.
+ pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
+ REMOVE_TASK_MODE_DESTROYING);
+ assertFalse(pinnedStack.isFocusable());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index c78fcd3..4b8dcc1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -103,7 +103,42 @@
assertEquals(mStack.getResumedActivity(), r);
r.setState(PAUSING, "testResumedActivity");
assertEquals(mStack.getResumedActivity(), null);
+ }
+ @Test
+ public void testResumedActivityFromTaskReparenting() {
+ final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
+ // Ensure moving task between two stacks updates resumed activity
+ r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
+ assertEquals(mStack.getResumedActivity(), r);
+
+ final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT,
+ false /* animate */, true /* deferResume*/,
+ "testResumedActivityFromTaskReparenting");
+
+ assertEquals(mStack.getResumedActivity(), null);
+ assertEquals(destStack.getResumedActivity(), r);
+ }
+
+ @Test
+ public void testResumedActivityFromActivityReparenting() {
+ final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
+ // Ensure moving task between two stacks updates resumed activity
+ r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
+ assertEquals(mStack.getResumedActivity(), r);
+
+ final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
+
+ mTask.removeActivity(r);
+ destTask.addActivityToTop(r);
+
+ assertEquals(mStack.getResumedActivity(), null);
+ assertEquals(destStack.getResumedActivity(), r);
}
@Test
@@ -543,5 +578,4 @@
assertEquals(expected, mStack.shouldSleepActivities());
}
-
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index d012bba..18e842f 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -23,6 +23,7 @@
import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
import static android.app.ActivityManager.START_PERMISSION_DENIED;
+import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_SWITCHES_CANCELED;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
@@ -34,6 +35,7 @@
import android.app.ActivityOptions;
import android.app.IApplicationThread;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.WindowLayout;
@@ -74,6 +76,8 @@
import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
import com.android.server.am.TaskRecord.TaskRecordFactory;
+import java.util.ArrayList;
+
/**
* Tests for the {@link ActivityStarter} class.
*
@@ -301,13 +305,14 @@
anyBoolean(), any(), any(), any());
// instrument the stack and task used.
- final ActivityStack stack = spy(mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */));
- final TaskRecord task =
- spy(new TaskBuilder(mService.mStackSupervisor).setStack(stack).build());
+ final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+ .setCreateStack(false)
+ .build();
// supervisor needs a focused stack.
- mService.mStackSupervisor.mFocusedStack = task.getStack();
+ mService.mStackSupervisor.mFocusedStack = stack;
// use factory that only returns spy task.
final TaskRecordFactory factory = mock(TaskRecordFactory.class);
@@ -322,14 +327,6 @@
doReturn(stack).when(mService.mStackSupervisor)
.getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
- // ignore the start request.
- doNothing().when(stack)
- .startActivityLocked(any(), any(), anyBoolean(), anyBoolean(), any());
-
- // ignore requests to create window container.
- doNothing().when(task).createWindowContainer(anyBoolean(), anyBoolean());
-
-
final Intent intent = new Intent();
intent.addFlags(launchFlags);
intent.setComponent(ActivityBuilder.getDefaultComponent());
@@ -448,4 +445,30 @@
// Ensure result is moving task to front.
assertEquals(result, START_TASK_TO_FRONT);
}
+
+ /**
+ * Tests activity is cleaned up properly in a task mode violation.
+ */
+ @Test
+ public void testTaskModeViolation() {
+ final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ assertNoTasks(display);
+
+ final ActivityStarter starter = prepareStarter(0);
+
+ final LockTaskController lockTaskController = mService.getLockTaskController();
+ doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
+
+ final int result = starter.setReason("testTaskModeViolation").execute();
+
+ assertEquals(START_RETURN_LOCK_TASK_MODE_VIOLATION, result);
+ assertNoTasks(display);
+ }
+
+ private void assertNoTasks(ActivityDisplay display) {
+ for (int i = display.getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = display.getChildAt(i);
+ assertTrue(stack.getAllTasks().isEmpty());
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 741901d..f5e61a1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
+import android.app.ActivityOptions;
import com.android.server.wm.DisplayWindowController;
import org.junit.Rule;
@@ -51,6 +52,9 @@
import android.support.test.InstrumentationRegistry;
import android.testing.DexmakerShareClassLoaderRule;
+
+import com.android.internal.app.IVoiceInteractor;
+
import com.android.server.AttributeCache;
import com.android.server.wm.AppWindowContainerController;
import com.android.server.wm.PinnedStackWindowController;
@@ -62,6 +66,7 @@
import org.junit.Before;
import org.mockito.MockitoAnnotations;
+
/**
* A base class to handle common operations in activity related unit tests.
*/
@@ -215,6 +220,7 @@
private int mTaskId = 0;
private int mUserId = 0;
private IVoiceInteractionSession mVoiceSession;
+ private boolean mCreateStack = true;
private ActivityStack mStack;
@@ -232,6 +238,15 @@
return this;
}
+ /**
+ * Set to {@code true} by default, set to {@code false} to prevent the task from
+ * automatically creating a parent stack.
+ */
+ TaskBuilder setCreateStack(boolean createStack) {
+ mCreateStack = createStack;
+ return this;
+ }
+
TaskBuilder setVoiceSession(IVoiceInteractionSession session) {
mVoiceSession = session;
return this;
@@ -258,7 +273,7 @@
}
TaskRecord build() {
- if (mStack == null) {
+ if (mStack == null && mCreateStack) {
mStack = mSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
}
@@ -276,17 +291,38 @@
intent.setComponent(mComponent);
intent.setFlags(mFlags);
- final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
+ final TestTaskRecord task = new TestTaskRecord(mSupervisor.mService, mTaskId, aInfo,
intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
task.userId = mUserId;
- mSupervisor.setFocusStackUnchecked("test", mStack);
- mStack.addTask(task, true, "creating test task");
- task.setStack(mStack);
- task.setWindowContainerController(mock(TaskWindowContainerController.class));
+
+ if (mStack != null) {
+ mSupervisor.setFocusStackUnchecked("test", mStack);
+ mStack.addTask(task, true, "creating test task");
+ task.setStack(mStack);
+ task.setWindowContainerController();
+ }
+
task.touchActiveTime();
return task;
}
+
+ private static class TestTaskRecord extends TaskRecord {
+ TestTaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info,
+ Intent _intent, IVoiceInteractionSession _voiceSession,
+ IVoiceInteractor _voiceInteractor) {
+ super(service, _taskId, info, _intent, _voiceSession, _voiceInteractor);
+ }
+
+ @Override
+ void createWindowContainer(boolean onTop, boolean showForAllUsers) {
+ setWindowContainerController();
+ }
+
+ private void setWindowContainerController() {
+ setWindowContainerController(mock(TaskWindowContainerController.class));
+ }
+ }
}
/**
@@ -295,6 +331,7 @@
*/
protected static class TestActivityManagerService extends ActivityManagerService {
private ClientLifecycleManager mLifecycleManager;
+ private LockTaskController mLockTaskController;
TestActivityManagerService(Context context) {
super(context);
@@ -314,6 +351,14 @@
return mLifecycleManager;
}
+ public LockTaskController getLockTaskController() {
+ if (mLockTaskController == null) {
+ mLockTaskController = spy(super.getLockTaskController());
+ }
+
+ return mLockTaskController;
+ }
+
void setLifecycleManager(ClientLifecycleManager manager) {
mLifecycleManager = manager;
}
@@ -444,7 +489,7 @@
}
/**
- * Overrided of {@link ActivityStack} that tracks test metrics, such as the number of times a
+ * Overridden {@link ActivityStack} that tracks test metrics, such as the number of times a
* method is called. Note that its functionality depends on the implementations of the
* construction arguments.
*/
@@ -530,5 +575,11 @@
return super.supportsSplitScreenWindowingMode();
}
}
+
+ @Override
+ void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
+ boolean newTask, boolean keepCurTransition,
+ ActivityOptions options) {
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index d37db20..4f18be7 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -26,7 +26,9 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.os.Process;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
@@ -395,7 +397,13 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
@@ -409,7 +417,13 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
@@ -425,7 +439,7 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[0][0];
+ packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
@@ -440,7 +454,7 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = null;
+ packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
@@ -453,7 +467,7 @@
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesNull_returnsFalse()
throws Exception {
PackageInfo packageInfo = new PackageInfo();
- packageInfo.signingCertificateHistory = null;
+ packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
@@ -467,7 +481,7 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[0][0];
+ packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
@@ -484,9 +498,13 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {
- {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
- };
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(
@@ -503,9 +521,13 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {
- {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
- };
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(
@@ -522,9 +544,13 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {
- {signature1Copy, signature2Copy}
- };
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {signature1Copy, signature2Copy},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(
@@ -541,9 +567,13 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {
- {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
- };
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = AppBackupUtils.signaturesMatch(
@@ -560,7 +590,13 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
@@ -579,7 +615,13 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_2},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ new Signature[] {SIGNATURE_1, SIGNATURE_2},
+ new int[] {0, 0}));
packageInfo.applicationInfo = new ApplicationInfo();
// we know signature1Copy is in history, and we want to assume it has
@@ -601,7 +643,13 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_2},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ new Signature[] {SIGNATURE_1, SIGNATURE_2},
+ new int[] {0, 0}));
packageInfo.applicationInfo = new ApplicationInfo();
// we know signature1Copy is in history, but we want to assume it does not have
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 5f052ce..2830a74 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -41,7 +41,9 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.os.Bundle;
import android.os.Process;
import android.platform.test.annotations.Presubmit;
@@ -371,7 +373,13 @@
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
- packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_2}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {FAKE_SIGNATURE_2},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
PackageManagerStub.sPackageInfo = packageInfo;
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
@@ -402,7 +410,13 @@
ApplicationInfo.FLAG_ALLOW_BACKUP | ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
packageInfo.applicationInfo.uid = Process.SYSTEM_UID;
packageInfo.applicationInfo.backupAgentName = "backup.agent";
- packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {FAKE_SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
PackageManagerStub.sPackageInfo = packageInfo;
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
@@ -434,7 +448,13 @@
ApplicationInfo.FLAG_ALLOW_BACKUP | ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
- packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {FAKE_SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
PackageManagerStub.sPackageInfo = packageInfo;
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
@@ -469,7 +489,13 @@
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
- packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {FAKE_SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.versionCode = 2;
PackageManagerStub.sPackageInfo = packageInfo;
@@ -507,7 +533,13 @@
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
- packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {FAKE_SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.versionCode = 1;
PackageManagerStub.sPackageInfo = packageInfo;
@@ -541,7 +573,13 @@
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
- packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {FAKE_SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.versionCode = 1;
PackageManagerStub.sPackageInfo = packageInfo;
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index fb25cf3..284d443 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -32,7 +32,9 @@
import android.os.PowerManager;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.MathUtils;
import android.util.Spline;
+import android.util.Slog;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -91,6 +93,29 @@
private static final float[] EMPTY_FLOAT_ARRAY = new float[0];
private static final int[] EMPTY_INT_ARRAY = new int[0];
+ private static final float MAXIMUM_GAMMA = 3.0f;
+ private static final int[] GAMMA_CORRECTION_LUX = {
+ 0,
+ 100,
+ 1000,
+ 2500,
+ 4000,
+ 4900,
+ 5000
+ };
+ private static final float[] GAMMA_CORRECTION_NITS = {
+ 1.0f,
+ 10.55f,
+ 96.5f,
+ 239.75f,
+ 383.0f,
+ 468.95f,
+ 478.5f,
+ };
+ private static final Spline GAMMA_CORRECTION_SPLINE = Spline.createSpline(
+ new float[] { 0.0f, 100.0f, 1000.0f, 2500.0f, 4000.0f, 4900.0f, 5000.0f },
+ new float[] { 0.035f, 0.035f, 0.221f, 0.523f, 0.797f, 0.980f, 1.0f });
+
@Test
public void testSimpleStrategyMappingAtControlPoints() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
@@ -325,7 +350,7 @@
}
// Now set the middle of the lux range to something just above the minimum.
- final float minBrightness = strategy.getBrightness(LUX_LEVELS[0]);
+ float minBrightness = strategy.getBrightness(LUX_LEVELS[0]);
strategy.addUserDataPoint(LUX_LEVELS[idx], minBrightness + 0.01f);
// Then make sure the curve is still monotonic.
@@ -340,7 +365,8 @@
// And that the lowest lux level still gives the absolute minimum brightness. This should
// be true assuming that there are more than two lux levels in the curve since we picked a
// brightness just barely above the minimum for the middle of the curve.
- assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.001 /*tolerance*/);
+ minBrightness = (float) MathUtils.pow(minBrightness, MAXIMUM_GAMMA); // Gamma correction.
+ assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.01 /*tolerance*/);
}
private static float[] toFloatArray(int[] vals) {
@@ -396,6 +422,9 @@
when(mockResources.getInteger(
com.android.internal.R.integer.config_screenBrightnessSettingMaximum))
.thenReturn(255);
+ when(mockResources.getFraction(
+ com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 1, 1))
+ .thenReturn(MAXIMUM_GAMMA);
return mockResources;
}
@@ -419,4 +448,121 @@
return mockArray;
}
+ // Gamma correction tests.
+ // x0 = 100 y0 = ~0.01
+ // x1 = 1000 y1 = ~0.20
+ // x2 = 2500 y2 = ~0.50
+ // x3 = 4000 y3 = ~0.80
+ // x4 = 4900 y4 = ~0.99
+
+ @Test
+ public void testGammaCorrectionLowChangeAtCenter() {
+ // If we set a user data point at (x2, y2^0.5), i.e. gamma = 0.5, it should bump the rest
+ // of the spline accordingly.
+ final int x1 = 1000;
+ final int x2 = 2500;
+ final int x3 = 4000;
+ final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1);
+ final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
+ final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
+ Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
+ DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+ // Let's start with a sanity check:
+ assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */);
+ assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
+ assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */);
+ // OK, let's roll:
+ float gamma = 0.5f;
+ strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma));
+ assertEquals(MathUtils.pow(y1, gamma), strategy.getBrightness(x1), 0.01f /* tolerance */);
+ assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */);
+ assertEquals(MathUtils.pow(y3, gamma), strategy.getBrightness(x3), 0.01f /* tolerance */);
+ // The adjustment should be +0.63 (manual calculation).
+ assertEquals(+0.63f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+ }
+
+ @Test
+ public void testGammaCorrectionHighChangeAtCenter() {
+ // This time we set a user data point at (x2, y2^0.25), i.e. gamma = 0.3 (the minimum),
+ // which should bump the rest of the spline accordingly, and further correct x2 to hit
+ // y2^0.25 (not y2^0.3).
+ final int x1 = 1000;
+ final int x2 = 2500;
+ final int x3 = 4000;
+ final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1);
+ final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
+ final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
+ Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
+ DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+ // Sanity check:
+ assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */);
+ assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
+ assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */);
+ // Let's roll:
+ float gamma = 0.25f;
+ final float minGamma = 1.0f / MAXIMUM_GAMMA;
+ strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma));
+ assertEquals(MathUtils.pow(y1, minGamma), strategy.getBrightness(x1),
+ 0.01f /* tolerance */);
+ assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2),
+ 0.01f /* tolerance */);
+ assertEquals(MathUtils.pow(y3, minGamma), strategy.getBrightness(x3),
+ 0.01f /* tolerance */);
+ // The adjustment should be +1.0 (maximum adjustment).
+ assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+ }
+
+ @Test
+ public void testGammaCorrectionExtremeChangeAtCenter() {
+ // Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we
+ // just make sure the adjustment reflects the change.
+ Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
+ DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+ assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+ strategy.addUserDataPoint(2500, 1.0f);
+ assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+ strategy.addUserDataPoint(2500, 0.0f);
+ assertEquals(-1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+ }
+
+ @Test
+ public void testGammaCorrectionChangeAtEdges() {
+ // The algorithm behaves differently at the edges, because gamma correction there tends to
+ // be extreme. If we add a user data point at (x0, y0+0.3), the adjustment should be
+ // 0.3*2 = 0.6, resulting in a gamma of 3**-0.6 = ~0.52.
+ final int x0 = 100;
+ final int x2 = 2500;
+ final int x4 = 4900;
+ final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0);
+ final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
+ final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4);
+ Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
+ DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+ // Sanity, as per tradition:
+ assertEquals(y0, strategy.getBrightness(x0), 0.01f /* tolerance */);
+ assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
+ assertEquals(y4, strategy.getBrightness(x4), 0.01f /* tolerance */);
+ // Rollin':
+ float increase = 0.3f;
+ float adjustment = increase * 2;
+ float gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment);
+ strategy.addUserDataPoint(x0, y0 + increase);
+ assertEquals(y0 + increase, strategy.getBrightness(x0), 0.01f /* tolerance */);
+ assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */);
+ assertEquals(MathUtils.pow(y4, gamma), strategy.getBrightness(x4), 0.01f /* tolerance */);
+ assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+ // Similarly, if we set a user data point at (x4, 1.0), the adjustment should be (1-y4)*2.
+ increase = 1.0f - y4;
+ adjustment = increase * 2;
+ gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment);
+ strategy.addUserDataPoint(x4, 1.0f);
+ assertEquals(MathUtils.pow(y0, gamma), strategy.getBrightness(x0), 0.01f /* tolerance */);
+ assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */);
+ assertEquals(1.0f, strategy.getBrightness(x4), 0.01f /* tolerance */);
+ assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index ee83b62..998ffa0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -58,11 +58,13 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -102,6 +104,8 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -1039,8 +1043,13 @@
pi.versionCode = version;
pi.applicationInfo.setVersionCode(version);
pi.signatures = null;
- pi.signingCertificateHistory = new Signature[][] {genSignatures(signatures)};
-
+ pi.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ genSignatures(signatures),
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
return pi;
}
@@ -1128,7 +1137,7 @@
if (getSignatures) {
ret.signatures = null;
- ret.signingCertificateHistory = pi.signingCertificateHistory;
+ ret.signingInfo = pi.signingInfo;
}
return ret;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
index cd7feea..203b2ca 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
@@ -76,13 +76,13 @@
mMyPackage, mMyUserId, /*signature*/ false);
assertEquals(mMyPackage, pi.packageName);
assertNull(pi.signatures);
- assertNull(pi.signingCertificateHistory);
+ assertNull(pi.signingInfo);
pi = mShortcutService.getPackageInfo(
mMyPackage, mMyUserId, /*signature*/ true);
assertEquals(mMyPackage, pi.packageName);
assertNull(pi.signatures);
- assertNotNull(pi.signingCertificateHistory);
+ assertNotNull(pi.signingInfo);
pi = mShortcutService.getPackageInfo(
"no.such.package", mMyUserId, /*signature*/ true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
index 36e4753..b8922eb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -32,6 +32,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.os.BaseBundle;
import android.os.Bundle;
import android.os.Handler;
@@ -68,6 +69,7 @@
@LargeTest
public class SuspendPackagesTest {
private static final String TAG = SuspendPackagesTest.class.getSimpleName();
+ private static final String TEST_APP_LABEL = "Suspend Test App";
private static final String TEST_APP_PACKAGE_NAME = SuspendTestReceiver.PACKAGE_NAME;
private static final String[] PACKAGES_TO_SUSPEND = new String[]{TEST_APP_PACKAGE_NAME};
@@ -242,7 +244,7 @@
}
@Test
- public void testIsPackageSuspended() {
+ public void testIsPackageSuspended() throws Exception {
suspendTestPackage(null, null, null);
assertTrue("isPackageSuspended is false",
mPackageManager.isPackageSuspended(TEST_APP_PACKAGE_NAME));
@@ -297,7 +299,7 @@
intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
final PersistableBundle extras2 = getExtras("testMyPackageSuspendedOnChangingExtras", 2,
"2", 0.2);
- mPackageManager.setSuspendedPackageAppExtras(TEST_APP_PACKAGE_NAME, extras2);
+ suspendTestPackage(extras2, null, null);
intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
@@ -370,8 +372,8 @@
}
@Override
- public void onPackagesSuspended(String[] packageNames, Bundle launcherExtras,
- UserHandle user) {
+ public void onPackagesSuspended(String[] packageNames, UserHandle user,
+ Bundle launcherExtras) {
final StringBuilder errorString = new StringBuilder();
if (!Arrays.equals(packageNames, PACKAGES_TO_SUSPEND)) {
errorString.append("Received unexpected packageNames in onPackagesSuspended:");
@@ -446,23 +448,24 @@
turnScreenOn();
mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED,
ACTION_REPORT_TEST_ACTIVITY_STARTED);
- final String testMessage = "This is a test message";
+ final String testMessage = "This is a test message to report suspension of %1$s";
suspendTestPackage(null, null, testMessage);
startTestAppActivity();
assertNull("No broadcast was expected from app", mAppCommsReceiver.pollForIntent(2));
- assertNotNull("Given dialog message not shown",
- mUiDevice.wait(Until.findObject(By.text(testMessage)), 5000));
- final String buttonText = "More details";
+ assertNotNull("Given dialog message not shown", mUiDevice.wait(
+ Until.findObject(By.text(String.format(testMessage, TEST_APP_LABEL))), 5000));
+ final String buttonText = mContext.getResources().getString(Resources.getSystem()
+ .getIdentifier("app_suspended_more_details", "string", "android"));
final UiObject2 moreDetailsButton = mUiDevice.findObject(
By.clickable(true).text(buttonText));
- assertNotNull("\"More Details\" button not shown", moreDetailsButton);
+ assertNotNull(buttonText + " button not shown", moreDetailsButton);
moreDetailsButton.click();
final Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
- assertEquals("\"More Details\" activity start not reported",
+ assertEquals(buttonText + " activity start not reported",
ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED, intentFromApp.getAction());
final String receivedPackageName = intentFromApp.getStringExtra(
EXTRA_RECEIVED_PACKAGE_NAME);
- assertEquals("Wrong package name received by \"More Details\" activity",
+ assertEquals("Wrong package name received by " + buttonText + " activity",
TEST_APP_PACKAGE_NAME, receivedPackageName);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
index 1ac7cb8..caa1d02 100644
--- a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -27,8 +27,10 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
import android.content.pm.PackageParser.Package;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.test.MoreAsserts;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
@@ -94,7 +96,13 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = BackupUtils.signaturesMatch(null, packageInfo,
@@ -108,7 +116,13 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -125,7 +139,7 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[0][0];
+ packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -142,7 +156,7 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = null;
+ packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -158,7 +172,7 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = null;
+ packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
boolean result = BackupUtils.signaturesMatch(null, packageInfo,
@@ -172,7 +186,7 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[0][0];
+ packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -186,9 +200,13 @@
public void signaturesMatch_equalSignatures_returnsTrue() throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {
- {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
- };
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -205,9 +223,13 @@
public void signaturesMatch_extraSignatureInTarget_returnsTrue() throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {
- {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
- };
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -223,7 +245,13 @@
public void signaturesMatch_extraSignatureInStored_returnsFalse() throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1, SIGNATURE_2}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1, SIGNATURE_2},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -240,9 +268,13 @@
public void signaturesMatch_oneNonMatchingSignature_returnsFalse() throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {
- {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
- };
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -260,7 +292,13 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
@@ -279,7 +317,13 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1, SIGNATURE_2},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
// we know SIGNATURE_1 is in history, and we want to assume it has
@@ -301,7 +345,13 @@
throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
- packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+ packageInfo.signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[] {SIGNATURE_1, SIGNATURE_2},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
packageInfo.applicationInfo = new ApplicationInfo();
// we know SIGNATURE_1 is in history, but we want to assume it does not have
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml
index ce6a27a..61058e6 100644
--- a/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.servicestests.apps.suspendtestapp">
- <application>
+ <application android:label="Suspend Test App">
<activity android:name=".SuspendTestActivity"
android:exported="true" />
<receiver android:name=".SuspendTestReceiver"
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceClientPermissionsTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceClientPermissionsTest.java
new file mode 100644
index 0000000..1efa415
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceClientPermissionsTest.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT 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.slice;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.util.Xml.Encoding;
+
+import com.android.server.UiServiceTestCase;
+import com.android.server.slice.SlicePermissionManager.PkgUser;
+import com.android.server.slice.SliceClientPermissions.SliceAuthority;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class SliceClientPermissionsTest extends UiServiceTestCase {
+
+ @Test
+ public void testRemoveBasic() {
+ PkgUser pkg = new PkgUser("com.android.pkg", 0);
+ DirtyTracker tracker = mock(DirtyTracker.class);
+ SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+ Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("com.android.pkg.slices").build();
+
+ PkgUser testPkg = new PkgUser("other", 2);
+
+ client.grantUri(base.buildUpon()
+ .appendPath("first")
+ .build(), testPkg);
+ client.revokeUri(base.buildUpon()
+ .appendPath("first")
+ .build(), testPkg);
+
+ assertFalse(client.hasPermission(base.buildUpon()
+ .appendPath("first")
+ .appendPath("third")
+ .build(), testPkg.getUserId()));
+
+ ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
+ assertEquals(0, authorities.get(0).getPaths().size());
+ }
+
+ @Test
+ public void testRemoveSubtrees() {
+ PkgUser pkg = new PkgUser("com.android.pkg", 0);
+ DirtyTracker tracker = mock(DirtyTracker.class);
+ SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+ Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("com.android.pkg.slices").build();
+
+ PkgUser testPkg = new PkgUser("other", 2);
+
+ client.grantUri(base.buildUpon()
+ .appendPath("first")
+ .appendPath("second")
+ .build(), testPkg);
+ client.grantUri(base.buildUpon()
+ .appendPath("first")
+ .appendPath("third")
+ .build(), testPkg);
+ client.revokeUri(base.buildUpon()
+ .appendPath("first")
+ .build(), testPkg);
+
+ assertFalse(client.hasPermission(base.buildUpon()
+ .appendPath("first")
+ .appendPath("fourth")
+ .build(), testPkg.getUserId()));
+
+ ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
+ assertEquals(0, authorities.get(0).getPaths().size());
+ }
+
+ @Test
+ public void testAddConsolidate_addFirst() {
+ PkgUser pkg = new PkgUser("com.android.pkg", 0);
+ DirtyTracker tracker = mock(DirtyTracker.class);
+ SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+ Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("com.android.pkg.slices").build();
+
+ PkgUser testPkg = new PkgUser("other", 2);
+
+ client.grantUri(base.buildUpon()
+ .appendPath("first")
+ .build(), testPkg);
+ client.grantUri(base.buildUpon()
+ .appendPath("first")
+ .appendPath("second")
+ .build(), testPkg);
+
+ assertTrue(client.hasPermission(base.buildUpon()
+ .appendPath("first")
+ .appendPath("third")
+ .build(), testPkg.getUserId()));
+
+ ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
+ assertEquals(1, authorities.get(0).getPaths().size());
+ }
+
+ @Test
+ public void testAddConsolidate_addSecond() {
+ PkgUser pkg = new PkgUser("com.android.pkg", 0);
+ DirtyTracker tracker = mock(DirtyTracker.class);
+ SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+ Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("com.android.pkg.slices").build();
+
+ PkgUser testPkg = new PkgUser("other", 2);
+
+ client.grantUri(base.buildUpon()
+ .appendPath("first")
+ .appendPath("second")
+ .build(), testPkg);
+ client.grantUri(base.buildUpon()
+ .appendPath("first")
+ .build(), testPkg);
+
+ assertTrue(client.hasPermission(base.buildUpon()
+ .appendPath("first")
+ .appendPath("third")
+ .build(), testPkg.getUserId()));
+
+ ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
+ assertEquals(1, authorities.get(0).getPaths().size());
+ }
+
+ @Test
+ public void testDirty_addAuthority() {
+ PkgUser pkg = new PkgUser("com.android.pkg", 0);
+ DirtyTracker tracker = mock(DirtyTracker.class);
+ SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+
+ client.getOrCreateAuthority(new PkgUser("some_auth", 2), new PkgUser("com.pkg", 2));
+
+ verify(tracker).onPersistableDirty(eq(client));
+ }
+
+ @Test
+ public void testDirty_addPkg() {
+ PkgUser pkg = new PkgUser("com.android.pkg", 0);
+ DirtyTracker tracker = mock(DirtyTracker.class);
+ SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+
+ SliceAuthority auth = client.getOrCreateAuthority(
+ new PkgUser("some_auth", 2),
+ new PkgUser("com.pkg", 2));
+ clearInvocations(tracker);
+
+ auth.addPath(Arrays.asList("/something/"));
+
+ verify(tracker).onPersistableDirty(eq(client));
+ }
+
+ @Test
+ public void testCreation() {
+ SliceClientPermissions client = createClient();
+ ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
+ authorities.sort(Comparator.comparing(SliceAuthority::getAuthority));
+
+ assertEquals(2, authorities.size());
+ assertEquals("com.android.pkg", authorities.get(0).getAuthority());
+ assertEquals("com.android.pkg.slices", authorities.get(1).getAuthority());
+
+ assertEquals(1, authorities.get(0).getPaths().size());
+ assertEquals(2, authorities.get(1).getPaths().size());
+ }
+
+ @Test
+ public void testSerialization() throws XmlPullParserException, IOException {
+ SliceClientPermissions client = createClient();
+ client.setHasFullAccess(true);
+
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
+ serializer.setOutput(output, Encoding.UTF_8.name());
+
+ client.writeTo(serializer);
+ serializer.flush();
+
+ ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
+ XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+ parser.setInput(input, Encoding.UTF_8.name());
+
+ SliceClientPermissions deser = SliceClientPermissions.createFrom(parser,
+ mock(DirtyTracker.class));
+
+ assertEquivalent(client, deser);
+ }
+
+ private void assertEquivalent(SliceClientPermissions o1, SliceClientPermissions o2) {
+ assertEquals(o1.getPkg(), o2.getPkg());
+ ArrayList<SliceAuthority> a1 = new ArrayList<>(o1.getAuthorities());
+ ArrayList<SliceAuthority> a2 = new ArrayList<>(o2.getAuthorities());
+ a1.sort(Comparator.comparing(SliceAuthority::getAuthority));
+ a2.sort(Comparator.comparing(SliceAuthority::getAuthority));
+ assertEquals(a1, a2);
+ }
+
+ private static SliceClientPermissions createClient() {
+ PkgUser pkg = new PkgUser("com.android.pkg", 2);
+ DirtyTracker tracker = mock(DirtyTracker.class);
+ SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+
+ SliceAuthority auth = client.getOrCreateAuthority(
+ new PkgUser("com.android.pkg.slices", 3),
+ new PkgUser("com.android.pkg", 3));
+ auth.addPath(Arrays.asList("/something/"));
+ auth.addPath(Arrays.asList("/something/else"));
+
+ auth = client.getOrCreateAuthority(
+ new PkgUser("com.android.pkg", 3),
+ new PkgUser("com.pkg", 1));
+ auth.addPath(Arrays.asList("/somewhere"));
+ return client;
+ }
+
+}
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
new file mode 100644
index 0000000..5443e73
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.server.slice;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.os.FileUtils;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.util.Log;
+import android.util.Xml.Encoding;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class SlicePermissionManagerTest extends UiServiceTestCase {
+
+ @Test
+ public void testBackup() throws XmlPullParserException, IOException {
+ File sliceDir = new File(mContext.getDataDir(), "system/slices");
+ Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("authority")
+ .path("something").build();
+ SlicePermissionManager permissions = new SlicePermissionManager(mContext,
+ TestableLooper.get(this).getLooper(), sliceDir);
+
+ permissions.grantFullAccess("com.android.mypkg", 10);
+ permissions.grantSliceAccess("com.android.otherpkg", 0, "com.android.lastpkg", 1, uri);
+
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
+ serializer.setOutput(output, Encoding.UTF_8.name());
+
+
+ TestableLooper.get(this).processAllMessages();
+ permissions.writeBackup(serializer);
+ serializer.flush();
+
+ ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
+ XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+ parser.setInput(input, Encoding.UTF_8.name());
+
+ permissions = new SlicePermissionManager(mContext,
+ TestableLooper.get(this).getLooper());
+ permissions.readRestore(parser);
+
+ assertTrue(permissions.hasFullAccess("com.android.mypkg", 10));
+ assertTrue(permissions.hasPermission("com.android.otherpkg", 0,
+ ContentProvider.maybeAddUserId(uri, 1)));
+ permissions.removePkg("com.android.lastpkg", 1);
+ assertFalse(permissions.hasPermission("com.android.otherpkg", 0,
+ ContentProvider.maybeAddUserId(uri, 1)));
+
+ // Cleanup.
+ assertTrue(FileUtils.deleteContentsAndDir(sliceDir));
+ }
+
+}
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceProviderPermissionsTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceProviderPermissionsTest.java
new file mode 100644
index 0000000..5775991
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceProviderPermissionsTest.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 com.android.server.slice;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.util.Xml.Encoding;
+
+import com.android.server.UiServiceTestCase;
+import com.android.server.slice.SlicePermissionManager.PkgUser;
+import com.android.server.slice.SliceProviderPermissions.SliceAuthority;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class SliceProviderPermissionsTest extends UiServiceTestCase {
+
+ @Test
+ public void testDirty_addAuthority() {
+ PkgUser pkg = new PkgUser("com.android.pkg", 0);
+ DirtyTracker tracker = mock(DirtyTracker.class);
+ SliceProviderPermissions provider = new SliceProviderPermissions(pkg, tracker);
+
+ provider.getOrCreateAuthority("some_auth");
+
+ verify(tracker).onPersistableDirty(eq(provider));
+ }
+
+ @Test
+ public void testDirty_addPkg() {
+ PkgUser pkg = new PkgUser("com.android.pkg", 0);
+ DirtyTracker tracker = mock(DirtyTracker.class);
+ SliceProviderPermissions provider = new SliceProviderPermissions(pkg, tracker);
+
+ SliceAuthority auth = provider.getOrCreateAuthority("some_auth");
+ clearInvocations(tracker);
+
+ auth.addPkg(new PkgUser("pkg", 0));
+
+ verify(tracker).onPersistableDirty(eq(provider));
+ }
+
+ @Test
+ public void testCreation() {
+ SliceProviderPermissions provider = createProvider();
+ ArrayList<SliceAuthority> authorities = new ArrayList<>(provider.getAuthorities());
+ authorities.sort(Comparator.comparing(SliceAuthority::getAuthority));
+
+ assertEquals(2, authorities.size());
+ assertEquals("com.android.pkg", authorities.get(0).getAuthority());
+ assertEquals("com.android.pkg.slices", authorities.get(1).getAuthority());
+
+ assertEquals(1, authorities.get(0).getPkgs().size());
+ assertEquals(2, authorities.get(1).getPkgs().size());
+ }
+
+ @Test
+ public void testSerialization() throws XmlPullParserException, IOException {
+ SliceProviderPermissions provider = createProvider();
+
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
+ serializer.setOutput(output, Encoding.UTF_8.name());
+
+ provider.writeTo(serializer);
+ serializer.flush();
+
+ ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
+ XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+ parser.setInput(input, Encoding.UTF_8.name());
+
+ SliceProviderPermissions deser = SliceProviderPermissions.createFrom(parser,
+ mock(DirtyTracker.class));
+
+ assertEquivalent(provider, deser);
+ }
+
+ private void assertEquivalent(SliceProviderPermissions o1, SliceProviderPermissions o2) {
+ assertEquals(o1.getPkg(), o2.getPkg());
+ assertEquals(o1.getAuthorities(), o2.getAuthorities());
+ }
+
+ private static SliceProviderPermissions createProvider() {
+ PkgUser pkg = new PkgUser("com.android.pkg", 2);
+ DirtyTracker tracker = mock(DirtyTracker.class);
+ SliceProviderPermissions provider = new SliceProviderPermissions(pkg, tracker);
+
+ SliceAuthority auth = provider.getOrCreateAuthority("com.android.pkg.slices");
+ auth.addPkg(new PkgUser("com.example.pkg", 0));
+ auth.addPkg(new PkgUser("example.pkg.com", 10));
+
+ auth = provider.getOrCreateAuthority("com.android.pkg");
+ auth.addPkg(new PkgUser("com.example.pkg", 2));
+ return provider;
+ }
+
+}
\ No newline at end of file
diff --git a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
index 74d8e12..3b08505 100644
--- a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
@@ -461,7 +461,7 @@
long token = dump.start(idName, id);
dump.write("connected_to_adb", UsbDebuggingManagerProto.CONNECTED_TO_ADB, mThread != null);
- writeStringIfNotNull(dump, "last_key_received", UsbDebuggingManagerProto.LAST_KEY_RECEVIED,
+ writeStringIfNotNull(dump, "last_key_received", UsbDebuggingManagerProto.LAST_KEY_RECEIVED,
mFingerprints);
try {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 1160943..cd524a5 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -926,25 +926,24 @@
Slog.w(TAG, mPuuid + ": Dropped operation as too many operations were "
+ "run in last 24 hours");
}
- return;
- }
+ } else {
+ mNumOps.addOp(currentTime);
- mNumOps.addOp(currentTime);
+ // Find a free opID
+ int opId = mNumTotalOpsPerformed;
+ do {
+ mNumTotalOpsPerformed++;
+ } while (mRunningOpIds.contains(opId));
- // Find a free opID
- int opId = mNumTotalOpsPerformed;
- do {
- mNumTotalOpsPerformed++;
- } while (mRunningOpIds.contains(opId));
+ // Run OP
+ try {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId);
- // Run OP
- try {
- if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId);
-
- op.run(opId, mService);
- mRunningOpIds.add(opId);
- } catch (Exception e) {
- Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e);
+ op.run(opId, mService);
+ mRunningOpIds.add(opId);
+ } catch (Exception e) {
+ Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e);
+ }
}
// Unbind from service if no operations are left (i.e. if the operation failed)
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index cac9f2b..3b773b3 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -16,8 +16,6 @@
package android.telephony;
-import android.annotation.SystemApi;
-
/**
* Contains access network related constants.
*/
@@ -39,7 +37,6 @@
* Wireless transportation type
* @hide
*/
- @SystemApi
public static final class TransportType {
/** Wireless Wide Area Networks (i.e. Cellular) */
public static final int WWAN = 1;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index aa76eab..e244131 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1684,6 +1684,14 @@
"data_warning_threshold_bytes_long";
/**
+ * Controls if the device should automatically notify the user as they reach
+ * their cellular data warning. When set to {@code false} the carrier is
+ * expected to have implemented their own notification mechanism.
+ */
+ public static final String KEY_DATA_WARNING_NOTIFICATION_BOOL =
+ "data_warning_notification_bool";
+
+ /**
* Controls the cellular data limit.
* <p>
* If the user uses more than this amount of data in their billing cycle, as defined by
@@ -1698,6 +1706,22 @@
"data_limit_threshold_bytes_long";
/**
+ * Controls if the device should automatically notify the user as they reach
+ * their cellular data limit. When set to {@code false} the carrier is
+ * expected to have implemented their own notification mechanism.
+ */
+ public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL =
+ "data_limit_notification_bool";
+
+ /**
+ * Controls if the device should automatically notify the user when rapid
+ * cellular data usage is observed. When set to {@code false} the carrier is
+ * expected to have implemented their own notification mechanism.
+ */
+ public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL =
+ "data_rapid_notification_bool";
+
+ /**
* Offset to be reduced from rsrp threshold while calculating signal strength level.
* @hide
*/
@@ -2165,7 +2189,10 @@
sDefaults.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT);
sDefaults.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+ sDefaults.putBoolean(KEY_DATA_WARNING_NOTIFICATION_BOOL, true);
sDefaults.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+ sDefaults.putBoolean(KEY_DATA_LIMIT_NOTIFICATION_BOOL, true);
+ sDefaults.putBoolean(KEY_DATA_RAPID_NOTIFICATION_BOOL, true);
// Rat families: {GPRS, EDGE}, {EVDO, EVDO_A, EVDO_B}, {UMTS, HSPA, HSDPA, HSUPA, HSPAP},
// {LTE, LTE_CA}
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index bba779d..0e2e0ce 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -18,7 +18,6 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,7 +30,6 @@
* Description of a mobile network registration state
* @hide
*/
-@SystemApi
public class NetworkRegistrationState implements Parcelable {
/**
* Network domain
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index f7e6840..b431590 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -17,7 +17,6 @@
package android.telephony;
import android.annotation.CallSuper;
-import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
@@ -47,7 +46,6 @@
* </service>
* @hide
*/
-@SystemApi
public abstract class NetworkService extends Service {
private final String TAG = NetworkService.class.getSimpleName();
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
index dbad02f..ad3b00f 100644
--- a/telephony/java/android/telephony/NetworkServiceCallback.java
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -17,7 +17,6 @@
package android.telephony;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.os.RemoteException;
import android.telephony.NetworkService.NetworkServiceProvider;
@@ -33,7 +32,6 @@
*
* @hide
*/
-@SystemApi
public class NetworkServiceCallback {
private static final String mTag = NetworkServiceCallback.class.getSimpleName();
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index fa7988d..8ffdb21 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -17,7 +17,6 @@
package android.telephony;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Parcel;
@@ -1534,7 +1533,6 @@
* @return List of registration states
* @hide
*/
- @SystemApi
public List<NetworkRegistrationState> getNetworkRegistrationStates() {
synchronized (mNetworkRegistrationStates) {
return new ArrayList<>(mNetworkRegistrationStates);
@@ -1548,7 +1546,6 @@
* @return List of registration states.
* @hide
*/
- @SystemApi
public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
List<NetworkRegistrationState> list = new ArrayList<>();
@@ -1571,7 +1568,6 @@
* @return The matching NetworkRegistrationState.
* @hide
*/
- @SystemApi
public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) {
synchronized (mNetworkRegistrationStates) {
for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
index ef2a364..e8bbe42 100644
--- a/telephony/java/android/telephony/SubscriptionPlan.java
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -254,6 +254,9 @@
* @param period The period after which the plan automatically recurs.
*/
public static Builder createRecurring(ZonedDateTime start, Period period) {
+ if (period.isZero() || period.isNegative()) {
+ throw new IllegalArgumentException("Period " + period + " must be positive");
+ }
return new Builder(start, null, period);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e15d35b..956a5b1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2968,7 +2968,7 @@
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getGroupIdLevel1(mContext.getOpPackageName());
+ return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -5156,7 +5156,12 @@
* {@link #AUTHTYPE_EAP_SIM}
* @param data authentication challenge data, base64 encoded.
* See 3GPP TS 31.102 7.1.2 for more details.
- * @return the response of authentication, or null if not available
+ * @return the response of authentication. This value will be null in the following cases:
+ * Authentication error, incorrect MAC
+ * Authentication error, security context not supported
+ * Key freshness failure
+ * Authentication error, no memory space available
+ * Authentication error, no memory space available in EFMUK
*/
// TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
// READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
@@ -5177,7 +5182,13 @@
* {@link #AUTHTYPE_EAP_SIM}
* @param data authentication challenge data, base64 encoded.
* See 3GPP TS 31.102 7.1.2 for more details.
- * @return the response of authentication, or null if not available
+ * @return the response of authentication. This value will be null in the following cases only
+ * (see 3GPP TS 31.102 7.3.1):
+ * Authentication error, incorrect MAC
+ * Authentication error, security context not supported
+ * Key freshness failure
+ * Authentication error, no memory space available
+ * Authentication error, no memory space available in EFMUK
* @hide
*/
public String getIccAuthentication(int subId, int appType, int authType, String data) {
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 25f5133..acc2470 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.net.LinkAddress;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,7 +33,6 @@
*
* @hide
*/
-@SystemApi
public final class DataCallResponse implements Parcelable {
private final int mStatus;
private final int mSuggestedRetryTime;
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index e8597b2..dd274c5 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -16,7 +16,6 @@
package android.telephony.data;
-import android.annotation.SystemApi;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,7 +29,6 @@
*
* @hide
*/
-@SystemApi
public final class DataProfile implements Parcelable {
// The types indicating the data profile is used on GSM (3GPP) or CDMA (3GPP2) network.
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 4ca5ce3..0835f7d 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -20,7 +20,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.net.LinkProperties;
@@ -55,7 +54,6 @@
* </service>
* @hide
*/
-@SystemApi
public abstract class DataService extends Service {
private static final String TAG = DataService.class.getSimpleName();
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 4af31b5..bff8260 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -17,7 +17,6 @@
package android.telephony.data;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.net.LinkProperties;
import android.os.RemoteException;
import android.telephony.Rlog;
@@ -35,7 +34,6 @@
*
* @hide
*/
-@SystemApi
public class DataServiceCallback {
private static final String TAG = DataServiceCallback.class.getSimpleName();
@@ -125,7 +123,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/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index 602c796..9e3302b 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Intent;
import android.net.Uri;
import android.os.Parcel;
@@ -184,6 +185,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public Builder setServiceId(String serviceId) {
fileServiceId = serviceId;
return this;
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 0ed0820..93964f3 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -68,11 +68,6 @@
String getSubscriberIdForSubscriber(int subId, String callingPackage);
/**
- * Retrieves the Group Identifier Level1 for GSM phones.
- */
- String getGroupIdLevel1(String callingPackage);
-
- /**
* Retrieves the Group Identifier Level1 for GSM phones of a subId.
*/
String getGroupIdLevel1ForSubscriber(int subId, String callingPackage);
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index 9174014..ae3914e 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -102,7 +102,7 @@
Thread t = new Thread(() -> {
try {
WindowManagerGlobal.getWindowSession().relayout(window,
- window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, mTmpRect,
+ window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect,
mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
new Surface());
diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
new file mode 100644
index 0000000..e58811b
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static android.content.Intent.ACTION_CONFIGURATION_CHANGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.NetworkPolicy.SNOOZE_NEVER;
+import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
+
+import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
+import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
+import android.net.StringNetworkSpecifier;
+import android.os.Handler;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
+import android.util.DataUnit;
+import android.util.RecurrenceRule;
+
+import com.android.internal.R;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.net.NetworkStatsManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MultipathPolicyTrackerTest {
+ private static final Network TEST_NETWORK = new Network(123);
+ private static final int POLICY_SNOOZED = -100;
+
+ @Mock private Context mContext;
+ @Mock private Resources mResources;
+ @Mock private Handler mHandler;
+ @Mock private MultipathPolicyTracker.Dependencies mDeps;
+ @Mock private Clock mClock;
+ @Mock private ConnectivityManager mCM;
+ @Mock private NetworkPolicyManager mNPM;
+ @Mock private NetworkStatsManager mStatsManager;
+ @Mock private NetworkPolicyManagerInternal mNPMI;
+ @Mock private NetworkStatsManagerInternal mNetworkStatsManagerInternal;
+ @Mock private TelephonyManager mTelephonyManager;
+ private MockContentResolver mContentResolver;
+
+ private ArgumentCaptor<BroadcastReceiver> mConfigChangeReceiverCaptor;
+
+ private MultipathPolicyTracker mTracker;
+
+ private Clock mPreviousRecurrenceRuleClock;
+ private boolean mRecurrenceRuleClockMocked;
+
+ private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
+ when(mContext.getSystemServiceName(serviceClass)).thenReturn(serviceName);
+ when(mContext.getSystemService(serviceName)).thenReturn(service);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mPreviousRecurrenceRuleClock = RecurrenceRule.sClock;
+ RecurrenceRule.sClock = mClock;
+ mRecurrenceRuleClockMocked = true;
+
+ mConfigChangeReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+ when(mContext.registerReceiverAsUser(mConfigChangeReceiverCaptor.capture(),
+ any(), argThat(f -> f.hasAction(ACTION_CONFIGURATION_CHANGED)), any(), any()))
+ .thenReturn(null);
+
+ when(mDeps.getClock()).thenReturn(mClock);
+
+ when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+
+ mContentResolver = Mockito.spy(new MockContentResolver(mContext));
+ mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ Settings.Global.clearProviderForTest();
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+
+ mockService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class, mCM);
+ mockService(Context.NETWORK_POLICY_SERVICE, NetworkPolicyManager.class, mNPM);
+ mockService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class, mStatsManager);
+ mockService(Context.TELEPHONY_SERVICE, TelephonyManager.class, mTelephonyManager);
+
+ LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
+ LocalServices.addService(NetworkPolicyManagerInternal.class, mNPMI);
+
+ LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
+ LocalServices.addService(NetworkStatsManagerInternal.class, mNetworkStatsManagerInternal);
+
+ mTracker = new MultipathPolicyTracker(mContext, mHandler, mDeps);
+ }
+
+ @After
+ public void tearDown() {
+ // Avoid setting static clock to null (which should normally not be the case)
+ // if MockitoAnnotations.initMocks threw an exception
+ if (mRecurrenceRuleClockMocked) {
+ RecurrenceRule.sClock = mPreviousRecurrenceRuleClock;
+ }
+ mRecurrenceRuleClockMocked = false;
+ }
+
+ private void setDefaultQuotaGlobalSetting(long setting) {
+ Settings.Global.putInt(mContentResolver, NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES,
+ (int) setting);
+ }
+
+ private void testGetMultipathPreference(
+ long usedBytesToday, long subscriptionQuota, long policyWarning, long policyLimit,
+ long defaultGlobalSetting, long defaultResSetting, boolean roaming) {
+
+ // TODO: tests should not use ZoneId.systemDefault() once code handles TZ correctly.
+ final ZonedDateTime now = ZonedDateTime.ofInstant(
+ Instant.parse("2017-04-02T10:11:12Z"), ZoneId.systemDefault());
+ final ZonedDateTime startOfDay = now.truncatedTo(ChronoUnit.DAYS);
+ when(mClock.millis()).thenReturn(now.toInstant().toEpochMilli());
+ when(mClock.instant()).thenReturn(now.toInstant());
+ when(mClock.getZone()).thenReturn(ZoneId.systemDefault());
+
+ // Setup plan quota
+ when(mNPMI.getSubscriptionOpportunisticQuota(TEST_NETWORK, QUOTA_TYPE_MULTIPATH))
+ .thenReturn(subscriptionQuota);
+
+ // Setup user policy warning / limit
+ if (policyWarning != WARNING_DISABLED || policyLimit != LIMIT_DISABLED) {
+ final Instant recurrenceStart = Instant.parse("2017-04-01T00:00:00Z");
+ final RecurrenceRule recurrenceRule = new RecurrenceRule(
+ ZonedDateTime.ofInstant(
+ recurrenceStart,
+ ZoneId.systemDefault()),
+ null /* end */,
+ Period.ofMonths(1));
+ final boolean snoozeWarning = policyWarning == POLICY_SNOOZED;
+ final boolean snoozeLimit = policyLimit == POLICY_SNOOZED;
+ when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[] {
+ new NetworkPolicy(
+ NetworkTemplate.buildTemplateMobileWildcard(),
+ recurrenceRule,
+ snoozeWarning ? 0 : policyWarning,
+ snoozeLimit ? 0 : policyLimit,
+ snoozeWarning ? recurrenceStart.toEpochMilli() + 1 : SNOOZE_NEVER,
+ snoozeLimit ? recurrenceStart.toEpochMilli() + 1 : SNOOZE_NEVER,
+ SNOOZE_NEVER,
+ true /* metered */,
+ false /* inferred */)
+ });
+ } else {
+ when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
+ }
+
+ // Setup default quota in settings and resources
+ if (defaultGlobalSetting > 0) {
+ setDefaultQuotaGlobalSetting(defaultGlobalSetting);
+ }
+ when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes))
+ .thenReturn((int) defaultResSetting);
+
+ when(mNetworkStatsManagerInternal.getNetworkTotalBytes(
+ any(),
+ eq(startOfDay.toInstant().toEpochMilli()),
+ eq(now.toInstant().toEpochMilli()))).thenReturn(usedBytesToday);
+
+ ArgumentCaptor<ConnectivityManager.NetworkCallback> networkCallback =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ mTracker.start();
+ verify(mCM).registerNetworkCallback(any(), networkCallback.capture(), any());
+
+ // Simulate callback after capability changes
+ final NetworkCapabilities capabilities = new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new StringNetworkSpecifier("234"));
+ if (!roaming) {
+ capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING);
+ }
+ networkCallback.getValue().onCapabilitiesChanged(
+ TEST_NETWORK,
+ capabilities);
+ }
+
+ @Test
+ public void testGetMultipathPreference_SubscriptionQuota() {
+ testGetMultipathPreference(
+ DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */,
+ DataUnit.MEGABYTES.toBytes(14) /* subscriptionQuota */,
+ DataUnit.MEGABYTES.toBytes(100) /* policyWarning */,
+ LIMIT_DISABLED,
+ DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+ 2_500_000 /* defaultResSetting */,
+ false /* roaming */);
+
+ verify(mStatsManager, times(1)).registerUsageCallback(
+ any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+ }
+
+ @Test
+ public void testGetMultipathPreference_UserWarningQuota() {
+ testGetMultipathPreference(
+ DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
+ OPPORTUNISTIC_QUOTA_UNKNOWN,
+ // 29 days from Apr. 2nd to May 1st
+ DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyWarning */,
+ LIMIT_DISABLED,
+ DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+ 2_500_000 /* defaultResSetting */,
+ false /* roaming */);
+
+ // Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB
+ verify(mStatsManager, times(1)).registerUsageCallback(
+ any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+ }
+
+ @Test
+ public void testGetMultipathPreference_SnoozedWarningQuota() {
+ testGetMultipathPreference(
+ DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
+ OPPORTUNISTIC_QUOTA_UNKNOWN,
+ // 29 days from Apr. 2nd to May 1st
+ POLICY_SNOOZED /* policyWarning */,
+ DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyLimit */,
+ DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+ 2_500_000 /* defaultResSetting */,
+ false /* roaming */);
+
+ // Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB
+ verify(mStatsManager, times(1)).registerUsageCallback(
+ any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+ }
+
+ @Test
+ public void testGetMultipathPreference_SnoozedBothQuota() {
+ testGetMultipathPreference(
+ DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
+ OPPORTUNISTIC_QUOTA_UNKNOWN,
+ // 29 days from Apr. 2nd to May 1st
+ POLICY_SNOOZED /* policyWarning */,
+ POLICY_SNOOZED /* policyLimit */,
+ DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+ 2_500_000 /* defaultResSetting */,
+ false /* roaming */);
+
+ // Default global setting should be used: 12 - 7 = 5
+ verify(mStatsManager, times(1)).registerUsageCallback(
+ any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(5)), any(), any());
+ }
+
+ @Test
+ public void testGetMultipathPreference_SettingChanged() {
+ testGetMultipathPreference(
+ DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */,
+ OPPORTUNISTIC_QUOTA_UNKNOWN,
+ WARNING_DISABLED,
+ LIMIT_DISABLED,
+ -1 /* defaultGlobalSetting */,
+ DataUnit.MEGABYTES.toBytes(10) /* defaultResSetting */,
+ false /* roaming */);
+
+ verify(mStatsManager, times(1)).registerUsageCallback(
+ any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+
+ // Update setting
+ setDefaultQuotaGlobalSetting(DataUnit.MEGABYTES.toBytes(14));
+ mTracker.mSettingsObserver.onChange(
+ false, Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES));
+
+ // Callback must have been re-registered with new setting
+ verify(mStatsManager, times(1)).unregisterUsageCallback(any());
+ verify(mStatsManager, times(1)).registerUsageCallback(
+ any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+ }
+
+ @Test
+ public void testGetMultipathPreference_ResourceChanged() {
+ testGetMultipathPreference(
+ DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */,
+ OPPORTUNISTIC_QUOTA_UNKNOWN,
+ WARNING_DISABLED,
+ LIMIT_DISABLED,
+ -1 /* defaultGlobalSetting */,
+ DataUnit.MEGABYTES.toBytes(14) /* defaultResSetting */,
+ false /* roaming */);
+
+ verify(mStatsManager, times(1)).registerUsageCallback(
+ any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+
+ when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes))
+ .thenReturn((int) DataUnit.MEGABYTES.toBytes(16));
+
+ final BroadcastReceiver configChangeReceiver = mConfigChangeReceiverCaptor.getValue();
+ assertNotNull(configChangeReceiver);
+ configChangeReceiver.onReceive(mContext, new Intent());
+
+ // Uses the new setting (16 - 2 = 14MB)
+ verify(mStatsManager, times(1)).registerUsageCallback(
+ any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(14)), any(), any());
+ }
+}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 6142a7c..d643c69 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -355,6 +355,7 @@
@Test
public void canRequireProvisioning() {
setupForRequiredProvisioning();
+ sendConfigurationChanged();
assertTrue(mTethering.isTetherProvisioningRequired());
}
@@ -363,6 +364,7 @@
setupForRequiredProvisioning();
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(null);
+ sendConfigurationChanged();
// Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
// We therefore still require provisioning.
assertTrue(mTethering.isTetherProvisioningRequired());
@@ -372,6 +374,7 @@
public void toleratesCarrierConfigMissing() {
setupForRequiredProvisioning();
when(mCarrierConfigManager.getConfig()).thenReturn(null);
+ sendConfigurationChanged();
// We still have a provisioning app configured, so still require provisioning.
assertTrue(mTethering.isTetherProvisioningRequired());
}
@@ -411,6 +414,11 @@
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private void sendConfigurationChanged() {
+ final Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+ mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
private void verifyInterfaceServingModeStarted() throws Exception {
verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(1))
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 26248e5..70a47cf 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -50,6 +50,18 @@
return "\033[%sm" % (";".join(codes))
+def ident(raw):
+ """Strips superficial signature changes, giving us a strong key that
+ can be used to identify members across API levels."""
+ raw = raw.replace(" deprecated ", " ")
+ raw = raw.replace(" synchronized ", " ")
+ raw = raw.replace(" final ", " ")
+ raw = re.sub("<.+?>", "", raw)
+ if " throws " in raw:
+ raw = raw[:raw.index(" throws ")]
+ return raw
+
+
class Field():
def __init__(self, clazz, line, raw, blame):
self.clazz = clazz
@@ -69,8 +81,7 @@
self.value = raw[3].strip(';"')
else:
self.value = None
-
- self.ident = self.raw.replace(" deprecated ", " ")
+ self.ident = ident(self.raw)
def __hash__(self):
return hash(self.raw)
@@ -105,15 +116,7 @@
for r in raw[2:]:
if r == "throws": target = self.throws
else: target.append(r)
-
- # identity for compat purposes
- ident = self.raw
- ident = ident.replace(" deprecated ", " ")
- ident = ident.replace(" synchronized ", " ")
- ident = re.sub("<.+?>", "", ident)
- if " throws " in ident:
- ident = ident[:ident.index(" throws ")]
- self.ident = ident
+ self.ident = ident(self.raw)
def __hash__(self):
return hash(self.raw)
@@ -1469,6 +1472,40 @@
return failures
+def show_deprecations_at_birth(cur, prev):
+ """Show API deprecations at birth."""
+ global failures
+
+ # Remove all existing things so we're left with new
+ for prev_clazz in prev.values():
+ cur_clazz = cur[prev_clazz.fullname]
+
+ sigs = { i.ident: i for i in prev_clazz.ctors }
+ cur_clazz.ctors = [ i for i in cur_clazz.ctors if i.ident not in sigs ]
+ sigs = { i.ident: i for i in prev_clazz.methods }
+ cur_clazz.methods = [ i for i in cur_clazz.methods if i.ident not in sigs ]
+ sigs = { i.ident: i for i in prev_clazz.fields }
+ cur_clazz.fields = [ i for i in cur_clazz.fields if i.ident not in sigs ]
+
+ # Forget about class entirely when nothing new
+ if len(cur_clazz.ctors) == 0 and len(cur_clazz.methods) == 0 and len(cur_clazz.fields) == 0:
+ del cur[prev_clazz.fullname]
+
+ for clazz in cur.values():
+ if " deprecated " in clazz.raw and not clazz.fullname in prev:
+ error(clazz, None, None, "Found API deprecation at birth")
+
+ for i in clazz.ctors + clazz.methods + clazz.fields:
+ if " deprecated " in i.raw:
+ error(clazz, i, None, "Found API deprecation at birth")
+
+ print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
+ format(reset=True)))
+ for f in sorted(failures):
+ print failures[f]
+ print
+
+
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Enforces common Android public API design \
patterns. It ignores lint messages from a previous API level, if provided.")
@@ -1481,6 +1518,8 @@
help="Allow references to Google")
parser.add_argument("--show-noticed", action='store_const', const=True,
help="Show API changes noticed")
+ parser.add_argument("--show-deprecations-at-birth", action='store_const', const=True,
+ help="Show API deprecations at birth")
args = vars(parser.parse_args())
if args['no_color']:
@@ -1492,6 +1531,14 @@
current_file = args['current.txt']
previous_file = args['previous.txt']
+ if args['show_deprecations_at_birth']:
+ with current_file as f:
+ cur = _parse_stream(f)
+ with previous_file as f:
+ prev = _parse_stream(f)
+ show_deprecations_at_birth(cur, prev)
+ sys.exit()
+
with current_file as f:
cur_fail, cur_noticed = examine_stream(f)
if not previous_file is None:
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 057772f..4146e02 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -101,6 +101,7 @@
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
+ fprintf(out, "#include <mutex>\n");
fprintf(out, "#include <chrono>\n");
fprintf(out, "#include <thread>\n");
fprintf(out, "#include <log/log_event_list.h>\n");
@@ -150,9 +151,7 @@
fprintf(out, "};\n");
fprintf(out, "\n");
- fprintf(out,
- "static std::map<int, int> "
- "getAtomUidField() {\n");
+ fprintf(out, "static std::map<int, int> getAtomUidField() {\n");
fprintf(out, " std::map<int, int> uidField;\n");
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
atom != atoms.decls.end(); atom++) {
@@ -206,6 +205,11 @@
"AtomsInfo::kStateAtomsFieldOptions = "
"getStateAtomFieldOptions();\n");
+
+ fprintf(out, "int64_t lastRetryTimestampNs = -1;\n");
+ fprintf(out, "const int64_t kMinRetryIntervalNs = NS_PER_SEC * 60 * 20; // 20 minutes\n");
+ fprintf(out, "static std::mutex mLogdRetryMutex;\n");
+
// Print write methods
fprintf(out, "\n");
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
@@ -317,7 +321,7 @@
fprintf(out, "{\n");
fprintf(out, " int ret = 0;\n");
- fprintf(out, " for(int retry = 0; retry < 3; ++retry) {\n");
+ fprintf(out, " for(int retry = 0; retry < 2; ++retry) {\n");
fprintf(out, " ret = try_stats_write(code");
argIndex = 1;
@@ -340,8 +344,15 @@
}
fprintf(out, ");\n");
fprintf(out, " if (ret >= 0) { return retry; }\n");
- fprintf(out,
- " std::this_thread::sleep_for(std::chrono::milliseconds(10 + 10 * retry));\n");
+
+
+ fprintf(out, " {\n");
+ fprintf(out, " std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n");
+ fprintf(out, " if ((android::elapsedRealtimeNano() - lastRetryTimestampNs) <= "
+ "kMinRetryIntervalNs) break;\n");
+ fprintf(out, " lastRetryTimestampNs = android::elapsedRealtimeNano();\n");
+ fprintf(out, " }\n");
+ fprintf(out, " std::this_thread::sleep_for(std::chrono::milliseconds(10));\n");
fprintf(out, " }\n");
fprintf(out, " return ret;\n");
fprintf(out, "}\n");
@@ -408,7 +419,7 @@
fprintf(out, "{\n");
fprintf(out, " int ret = 0;\n");
- fprintf(out, " for(int retry = 0; retry < 3; ++retry) {\n");
+ fprintf(out, " for(int retry = 0; retry < 2; ++retry) {\n");
fprintf(out, " ret = try_stats_write_non_chained(code");
argIndex = 1;
@@ -419,8 +430,15 @@
}
fprintf(out, ");\n");
fprintf(out, " if (ret >= 0) { return retry; }\n");
- fprintf(out,
- " std::this_thread::sleep_for(std::chrono::milliseconds(10 + 10 * retry));\n");
+
+ fprintf(out, " {\n");
+ fprintf(out, " std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n");
+ fprintf(out, " if ((android::elapsedRealtimeNano() - lastRetryTimestampNs) <= "
+ "kMinRetryIntervalNs) break;\n");
+ fprintf(out, " lastRetryTimestampNs = android::elapsedRealtimeNano();\n");
+ fprintf(out, " }\n");
+
+ fprintf(out, " std::this_thread::sleep_for(std::chrono::milliseconds(10));\n");
fprintf(out, " }\n");
fprintf(out, " return ret;\n");
fprintf(out, "}\n");