Merge "Switch to skcms-based factories and getters in SkColorSpace"
diff --git a/Android.bp b/Android.bp
index 7e97e66..c202408 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1067,6 +1067,7 @@
"core/java/android/annotation/IntDef.java",
"core/java/android/annotation/NonNull.java",
"core/java/android/annotation/SystemApi.java",
+ "core/java/android/annotation/TestApi.java",
"core/java/android/os/HwBinder.java",
"core/java/android/os/HwBlob.java",
"core/java/android/os/HwParcel.java",
diff --git a/api/current.txt b/api/current.txt
index 00d4cb5..86e3021 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11191,6 +11191,7 @@
method public void registerCallback(android.content.pm.LauncherApps.Callback);
method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
+ method public boolean shouldHideFromSuggestions(java.lang.String, android.os.UserHandle);
method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
@@ -13736,6 +13737,7 @@
method public void drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint);
method public void drawTextRun(char[], int, int, int, int, float, float, boolean, android.graphics.Paint);
method public void drawTextRun(java.lang.CharSequence, int, int, int, int, float, float, boolean, android.graphics.Paint);
+ method public void drawTextRun(android.graphics.text.MeasuredText, int, int, int, int, float, float, boolean, android.graphics.Paint);
method public void drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint);
method public void enableZ();
method public boolean getClipBounds(android.graphics.Rect);
@@ -24175,8 +24177,11 @@
public static final class MediaCodec.CryptoException extends java.lang.RuntimeException {
ctor public MediaCodec.CryptoException(int, java.lang.String);
method public int getErrorCode();
+ field public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
field public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
+ field public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7
field public static final int ERROR_KEY_EXPIRED = 2; // 0x2
+ field public static final int ERROR_LOST_STATE = 9; // 0x9
field public static final int ERROR_NO_KEY = 1; // 0x1
field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
@@ -24517,6 +24522,22 @@
field public static final int REGULAR_CODECS = 0; // 0x0
}
+ public class MediaController2 implements java.lang.AutoCloseable {
+ ctor public MediaController2(android.content.Context, android.media.Session2Token);
+ ctor public MediaController2(android.content.Context, android.media.Session2Token, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback);
+ method public void cancelSessionCommand(java.lang.Object);
+ method public void close();
+ method public java.lang.Object sendSessionCommand(android.media.Session2Command, android.os.Bundle);
+ }
+
+ public static abstract class MediaController2.ControllerCallback {
+ ctor public MediaController2.ControllerCallback();
+ method public void onCommandResult(android.media.MediaController2, java.lang.Object, android.media.Session2Command, android.media.Session2Command.Result);
+ method public void onConnected(android.media.MediaController2, android.media.Session2CommandGroup);
+ method public void onDisconnected(android.media.MediaController2);
+ method public android.media.Session2Command.Result onSessionCommand(android.media.MediaController2, android.media.Session2Command, android.os.Bundle);
+ }
+
public final class MediaCrypto {
ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
method protected void finalize();
@@ -24624,6 +24645,7 @@
method public void setOnEventListener(android.media.MediaDrm.OnEventListener);
method public void setOnExpirationUpdateListener(android.media.MediaDrm.OnExpirationUpdateListener, android.os.Handler);
method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
+ method public void setOnSessionLostStateListener(android.media.MediaDrm.OnSessionLostStateListener, android.os.Handler);
method public void setPropertyByteArray(java.lang.String, byte[]);
method public void setPropertyString(java.lang.String, java.lang.String);
field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3
@@ -24742,6 +24764,10 @@
method public abstract void onKeyStatusChange(android.media.MediaDrm, byte[], java.util.List<android.media.MediaDrm.KeyStatus>, boolean);
}
+ public static abstract interface MediaDrm.OnSessionLostStateListener {
+ method public abstract void onSessionLostState(android.media.MediaDrm, byte[]);
+ }
+
public static final class MediaDrm.ProvisionRequest {
method public byte[] getData();
method public java.lang.String getDefaultUrl();
@@ -24750,6 +24776,12 @@
public static abstract class MediaDrm.SecurityLevel implements java.lang.annotation.Annotation {
}
+ public static final class MediaDrm.SessionException extends java.lang.RuntimeException {
+ ctor public MediaDrm.SessionException(int, java.lang.String);
+ method public int getErrorCode();
+ field public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1
+ }
+
public class MediaDrmException extends java.lang.Exception {
ctor public MediaDrmException(java.lang.String);
}
@@ -25805,6 +25837,37 @@
method public abstract void onScanCompleted(java.lang.String, android.net.Uri);
}
+ public class MediaSession2 implements java.lang.AutoCloseable {
+ method public void broadcastSessionCommand(android.media.Session2Command, android.os.Bundle);
+ method public void cancelSessionCommand(android.media.MediaSession2.ControllerInfo, java.lang.Object);
+ method public void close();
+ method public java.lang.String getSessionId();
+ method public android.media.Session2Token getSessionToken();
+ method public java.lang.Object sendSessionCommand(android.media.MediaSession2.ControllerInfo, android.media.Session2Command, android.os.Bundle);
+ }
+
+ public static final class MediaSession2.Builder {
+ ctor public MediaSession2.Builder(android.content.Context);
+ method public android.media.MediaSession2 build();
+ method public android.media.MediaSession2.Builder setId(java.lang.String);
+ method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent);
+ method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback);
+ }
+
+ public static final class MediaSession2.ControllerInfo {
+ method public java.lang.String getPackageName();
+ method public android.media.session.MediaSessionManager.RemoteUserInfo getRemoteUserInfo();
+ method public int getUid();
+ }
+
+ public static abstract class MediaSession2.SessionCallback {
+ ctor public MediaSession2.SessionCallback();
+ method public void onCommandResult(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.Object, android.media.Session2Command, android.media.Session2Command.Result);
+ method public android.media.Session2CommandGroup onConnect(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
+ method public void onDisconnected(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
+ method public android.media.Session2Command.Result onSessionCommand(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.Session2Command, android.os.Bundle);
+ }
+
public final class MediaSync {
ctor public MediaSync();
method public android.view.Surface createInputSurface();
@@ -26118,6 +26181,19 @@
method public android.media.Session2CommandGroup.Builder removeCommand(int);
}
+ public final class Session2Token implements android.os.Parcelable {
+ ctor public Session2Token(android.content.Context, android.content.ComponentName);
+ method public int describeContents();
+ method public java.lang.String getPackageName();
+ method public java.lang.String getServiceName();
+ method public int getType();
+ method public int getUid();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.Session2Token> CREATOR;
+ field public static final int TYPE_SESSION = 0; // 0x0
+ field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
+ }
+
public class SoundPool {
ctor public deprecated SoundPool(int, int, int);
method public final void autoPause();
@@ -43057,10 +43133,12 @@
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
field public static final java.lang.String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
+ field public static final java.lang.String EVENT_RTT_AUDIO_INDICATION_CHANGED = "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME";
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
+ field public static final java.lang.String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final java.lang.String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
@@ -44336,6 +44414,7 @@
method public int describeContents();
method public int getCdmaDbm();
method public int getCdmaEcio();
+ method public java.util.List<android.telephony.CellSignalStrength> getCellSignalStrengths();
method public int getEvdoDbm();
method public int getEvdoEcio();
method public int getEvdoSnr();
@@ -44778,6 +44857,9 @@
public static abstract class TelephonyManager.CellInfoCallback {
ctor public TelephonyManager.CellInfoCallback();
method public abstract void onCellInfo(java.util.List<android.telephony.CellInfo>);
+ method public void onError(int, java.lang.Throwable);
+ field public static final int ERROR_MODEM_ERROR = 2; // 0x2
+ field public static final int ERROR_TIMEOUT = 1; // 0x1
}
public static abstract class TelephonyManager.UssdResponseCallback {
@@ -52934,15 +53016,15 @@
package android.view.textclassifier {
- public final class ConversationActions implements android.os.Parcelable {
- ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>, java.lang.String);
+ public final class ConversationAction implements android.os.Parcelable {
method public int describeContents();
- method public java.util.List<android.view.textclassifier.ConversationActions.ConversationAction> getConversationActions();
- method public java.lang.String getId();
+ method public android.app.RemoteAction getAction();
+ method public float getConfidenceScore();
+ method public android.os.Bundle getExtras();
+ method public java.lang.CharSequence getTextReply();
+ method public java.lang.String getType();
method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
- field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
- field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification";
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationAction> CREATOR;
field public static final java.lang.String TYPE_CALL_PHONE = "call_phone";
field public static final java.lang.String TYPE_CREATE_REMINDER = "create_reminder";
field public static final java.lang.String TYPE_OPEN_URL = "open_url";
@@ -52955,24 +53037,22 @@
field public static final java.lang.String TYPE_VIEW_MAP = "view_map";
}
- public static final class ConversationActions.ConversationAction implements android.os.Parcelable {
- method public int describeContents();
- method public android.app.RemoteAction getAction();
- method public float getConfidenceScore();
- method public android.os.Bundle getExtras();
- method public java.lang.CharSequence getTextReply();
- method public java.lang.String getType();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.ConversationAction> CREATOR;
+ public static final class ConversationAction.Builder {
+ ctor public ConversationAction.Builder(java.lang.String);
+ method public android.view.textclassifier.ConversationAction build();
+ method public android.view.textclassifier.ConversationAction.Builder setAction(android.app.RemoteAction);
+ method public android.view.textclassifier.ConversationAction.Builder setConfidenceScore(float);
+ method public android.view.textclassifier.ConversationAction.Builder setExtras(android.os.Bundle);
+ method public android.view.textclassifier.ConversationAction.Builder setTextReply(java.lang.CharSequence);
}
- public static final class ConversationActions.ConversationAction.Builder {
- ctor public ConversationActions.ConversationAction.Builder(java.lang.String);
- method public android.view.textclassifier.ConversationActions.ConversationAction build();
- method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setAction(android.app.RemoteAction);
- method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setConfidenceScore(float);
- method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setExtras(android.os.Bundle);
- method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setTextReply(java.lang.CharSequence);
+ public final class ConversationActions implements android.os.Parcelable {
+ ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationAction>, java.lang.String);
+ method public int describeContents();
+ method public java.util.List<android.view.textclassifier.ConversationAction> getConversationActions();
+ method public java.lang.String getId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
}
public static final class ConversationActions.Message implements android.os.Parcelable {
@@ -53002,9 +53082,11 @@
method public java.lang.String getConversationId();
method public java.util.List<java.lang.String> getHints();
method public int getMaxSuggestions();
- method public android.view.textclassifier.ConversationActions.TypeConfig getTypeConfig();
+ method public android.view.textclassifier.TextClassifier.EntityConfig getTypeConfig();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Request> CREATOR;
+ field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
+ field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification";
}
public static final class ConversationActions.Request.Builder {
@@ -53013,23 +53095,7 @@
method public android.view.textclassifier.ConversationActions.Request.Builder setConversationId(java.lang.String);
method public android.view.textclassifier.ConversationActions.Request.Builder setHints(java.util.List<java.lang.String>);
method public android.view.textclassifier.ConversationActions.Request.Builder setMaxSuggestions(int);
- method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.ConversationActions.TypeConfig);
- }
-
- public static final class ConversationActions.TypeConfig implements android.os.Parcelable {
- method public int describeContents();
- method public java.util.Collection<java.lang.String> resolveTypes(java.util.Collection<java.lang.String>);
- method public boolean shouldIncludeTypesFromTextClassifier();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.TypeConfig> CREATOR;
- }
-
- public static final class ConversationActions.TypeConfig.Builder {
- ctor public ConversationActions.TypeConfig.Builder();
- method public android.view.textclassifier.ConversationActions.TypeConfig build();
- method public android.view.textclassifier.ConversationActions.TypeConfig.Builder includeTypesFromTextClassifier(boolean);
- method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>);
- method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>);
+ method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.TextClassifier.EntityConfig);
}
public final class SelectionEvent implements android.os.Parcelable {
@@ -53203,16 +53269,26 @@
}
public static final class TextClassifier.EntityConfig implements android.os.Parcelable {
- method public static android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>);
- method public static android.view.textclassifier.TextClassifier.EntityConfig createWithExplicitEntityList(java.util.Collection<java.lang.String>);
- method public static android.view.textclassifier.TextClassifier.EntityConfig createWithHints(java.util.Collection<java.lang.String>);
+ method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>);
+ method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig createWithExplicitEntityList(java.util.Collection<java.lang.String>);
+ method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig createWithHints(java.util.Collection<java.lang.String>);
method public int describeContents();
method public java.util.Collection<java.lang.String> getHints();
method public java.util.Collection<java.lang.String> resolveEntityListModifications(java.util.Collection<java.lang.String>);
+ method public boolean shouldIncludeTypesFromTextClassifier();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
}
+ public static final class TextClassifier.EntityConfig.Builder {
+ ctor public TextClassifier.EntityConfig.Builder();
+ method public android.view.textclassifier.TextClassifier.EntityConfig build();
+ method public android.view.textclassifier.TextClassifier.EntityConfig.Builder includeTypesFromTextClassifier(boolean);
+ method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>);
+ method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setHints(java.util.Collection<java.lang.String>);
+ method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>);
+ }
+
public final class TextClassifierEvent implements android.os.Parcelable {
method public int describeContents();
method public int[] getActionIndices();
diff --git a/api/system-current.txt b/api/system-current.txt
index f686604..78f3471 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1047,7 +1047,6 @@
method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
method public void setRoleNamesFromController(java.util.List<java.lang.String>);
- field public static final java.lang.String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
}
public abstract interface RoleManagerCallback {
@@ -1254,6 +1253,7 @@
field public static final java.lang.String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
+ field public static final java.lang.String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
field public static final java.lang.String ACTION_MANAGE_SPECIAL_APP_ACCESSES = "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES";
@@ -1284,11 +1284,12 @@
field public static final java.lang.String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
field public static final java.lang.String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
- field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
field public static final java.lang.String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
+ field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON";
field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
+ field public static final java.lang.String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
field public static final java.lang.String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
field public static final java.lang.String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
field public static final java.lang.String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
@@ -1461,6 +1462,7 @@
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public void sendDeviceCustomizationReadyBroadcast();
method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
+ method public java.lang.String[] setDistractingPackageRestrictions(java.lang.String[], int);
method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo);
@@ -1528,6 +1530,9 @@
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
field public static final int MATCH_INSTANT = 8388608; // 0x800000
+ field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
+ field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2
+ field public static final int RESTRICTION_NONE = 0; // 0x0
}
public static abstract class PackageManager.DexModuleRegisterCallback {
@@ -1535,6 +1540,9 @@
method public abstract void onDexModuleRegistered(java.lang.String, boolean, java.lang.String);
}
+ public static abstract class PackageManager.DistractionRestriction implements java.lang.annotation.Annotation {
+ }
+
public static abstract interface PackageManager.OnPermissionsChangedListener {
method public abstract void onPermissionsChanged(int);
}
@@ -4880,6 +4888,14 @@
field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR;
}
+ public final class RuntimePermissionUsageInfo implements android.os.Parcelable {
+ ctor public RuntimePermissionUsageInfo(java.lang.CharSequence, int);
+ method public int describeContents();
+ method public java.lang.CharSequence getName();
+ method public int getAppAccessCount();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionUsageInfo> CREATOR;
+ }
}
package android.permissionpresenterservice {
@@ -6202,6 +6218,40 @@
field public static final int WWAN = 1; // 0x1
}
+ public class CallAttributes implements android.os.Parcelable {
+ ctor public CallAttributes(android.telephony.PreciseCallState, int, android.telephony.CallQuality);
+ method public int describeContents();
+ method public android.telephony.CallQuality getCallQuality();
+ method public int getNetworkType();
+ method public android.telephony.PreciseCallState getPreciseCallState();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
+ }
+
+ public final class CallQuality implements android.os.Parcelable {
+ ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
+ method public int describeContents();
+ method public int getAverageRelativeJitter();
+ method public int getAverageRoundTripTime();
+ method public int getCallDuration();
+ method public int getCodecType();
+ method public int getDownlinkCallQualityLevel();
+ method public int getMaxRelativeJitter();
+ method public int getNumRtpPacketsNotReceived();
+ method public int getNumRtpPacketsReceived();
+ method public int getNumRtpPacketsTransmitted();
+ method public int getNumRtpPacketsTransmittedLost();
+ method public int getUplinkCallQualityLevel();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CALL_QUALITY_BAD = 4; // 0x4
+ field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
+ field public static final int CALL_QUALITY_FAIR = 2; // 0x2
+ field public static final int CALL_QUALITY_GOOD = 1; // 0x1
+ field public static final int CALL_QUALITY_NOT_AVAILABLE = 5; // 0x5
+ field public static final int CALL_QUALITY_POOR = 3; // 0x3
+ field public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
+ }
+
public class CarrierConfigManager {
method public static android.os.PersistableBundle getDefaultConfig();
method public void overrideConfig(int, android.os.PersistableBundle);
@@ -6467,12 +6517,14 @@
}
public class PhoneStateListener {
+ method public void onCallAttributesChanged(android.telephony.CallAttributes);
method public void onCallDisconnectCauseChanged(int, int);
method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
method public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState);
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
method public void onVoiceActivationStateChanged(int);
+ field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
field public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
@@ -7199,6 +7251,7 @@
method public void callSessionInviteParticipantsRequestDelivered();
method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionMayHandover(int, int);
+ method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile);
method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
@@ -7616,10 +7669,12 @@
method public int describeContents();
method public int getAudioDirection();
method public int getAudioQuality();
+ method public boolean getRttAudioSpeech();
method public int getRttMode();
method public int getVideoDirection();
method public int getVideoQuality();
method public boolean isRttCall();
+ method public void setRttAudioSpeech(boolean);
method public void setRttMode(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final int AUDIO_QUALITY_AMR = 1; // 0x1
diff --git a/api/test-current.txt b/api/test-current.txt
index 55705b6..1ef3096 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -421,6 +421,7 @@
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
+ field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
@@ -790,6 +791,134 @@
method public static java.io.File getStorageDirectory();
}
+ public abstract class HwBinder implements android.os.IHwBinder {
+ ctor public HwBinder();
+ method public static final void configureRpcThreadpool(long, boolean);
+ method public static void enableInstrumentation();
+ method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String) throws java.util.NoSuchElementException, android.os.RemoteException;
+ method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException;
+ method public static final void joinRpcThreadpool();
+ method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+ method public final void registerService(java.lang.String) throws android.os.RemoteException;
+ method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+ }
+
+ public class HwBlob {
+ ctor public HwBlob(int);
+ method public final void copyToBoolArray(long, boolean[], int);
+ method public final void copyToDoubleArray(long, double[], int);
+ method public final void copyToFloatArray(long, float[], int);
+ method public final void copyToInt16Array(long, short[], int);
+ method public final void copyToInt32Array(long, int[], int);
+ method public final void copyToInt64Array(long, long[], int);
+ method public final void copyToInt8Array(long, byte[], int);
+ method public final boolean getBool(long);
+ method public final double getDouble(long);
+ method public final float getFloat(long);
+ method public final short getInt16(long);
+ method public final int getInt32(long);
+ method public final long getInt64(long);
+ method public final byte getInt8(long);
+ method public final java.lang.String getString(long);
+ method public final long handle();
+ method public final void putBlob(long, android.os.HwBlob);
+ method public final void putBool(long, boolean);
+ method public final void putBoolArray(long, boolean[]);
+ method public final void putDouble(long, double);
+ method public final void putDoubleArray(long, double[]);
+ method public final void putFloat(long, float);
+ method public final void putFloatArray(long, float[]);
+ method public final void putInt16(long, short);
+ method public final void putInt16Array(long, short[]);
+ method public final void putInt32(long, int);
+ method public final void putInt32Array(long, int[]);
+ method public final void putInt64(long, long);
+ method public final void putInt64Array(long, long[]);
+ method public final void putInt8(long, byte);
+ method public final void putInt8Array(long, byte[]);
+ method public final void putNativeHandle(long, android.os.NativeHandle);
+ method public final void putString(long, java.lang.String);
+ method public static java.lang.Boolean[] wrapArray(boolean[]);
+ method public static java.lang.Long[] wrapArray(long[]);
+ method public static java.lang.Byte[] wrapArray(byte[]);
+ method public static java.lang.Short[] wrapArray(short[]);
+ method public static java.lang.Integer[] wrapArray(int[]);
+ method public static java.lang.Float[] wrapArray(float[]);
+ method public static java.lang.Double[] wrapArray(double[]);
+ }
+
+ public class HwParcel {
+ ctor public HwParcel();
+ method public final void enforceInterface(java.lang.String);
+ method public final boolean readBool();
+ method public final java.util.ArrayList<java.lang.Boolean> readBoolVector();
+ method public final android.os.HwBlob readBuffer(long);
+ method public final double readDouble();
+ method public final java.util.ArrayList<java.lang.Double> readDoubleVector();
+ method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean);
+ method public final android.os.NativeHandle readEmbeddedNativeHandle(long, long);
+ method public final float readFloat();
+ method public final java.util.ArrayList<java.lang.Float> readFloatVector();
+ method public final short readInt16();
+ method public final java.util.ArrayList<java.lang.Short> readInt16Vector();
+ method public final int readInt32();
+ method public final java.util.ArrayList<java.lang.Integer> readInt32Vector();
+ method public final long readInt64();
+ method public final java.util.ArrayList<java.lang.Long> readInt64Vector();
+ method public final byte readInt8();
+ method public final java.util.ArrayList<java.lang.Byte> readInt8Vector();
+ method public final android.os.NativeHandle readNativeHandle();
+ method public final java.util.ArrayList<android.os.NativeHandle> readNativeHandleVector();
+ method public final java.lang.String readString();
+ method public final java.util.ArrayList<java.lang.String> readStringVector();
+ method public final android.os.IHwBinder readStrongBinder();
+ method public final void release();
+ method public final void releaseTemporaryStorage();
+ method public final void send();
+ method public final void verifySuccess();
+ method public final void writeBool(boolean);
+ method public final void writeBoolVector(java.util.ArrayList<java.lang.Boolean>);
+ method public final void writeBuffer(android.os.HwBlob);
+ method public final void writeDouble(double);
+ method public final void writeDoubleVector(java.util.ArrayList<java.lang.Double>);
+ method public final void writeFloat(float);
+ method public final void writeFloatVector(java.util.ArrayList<java.lang.Float>);
+ method public final void writeInt16(short);
+ method public final void writeInt16Vector(java.util.ArrayList<java.lang.Short>);
+ method public final void writeInt32(int);
+ method public final void writeInt32Vector(java.util.ArrayList<java.lang.Integer>);
+ method public final void writeInt64(long);
+ method public final void writeInt64Vector(java.util.ArrayList<java.lang.Long>);
+ method public final void writeInt8(byte);
+ method public final void writeInt8Vector(java.util.ArrayList<java.lang.Byte>);
+ method public final void writeInterfaceToken(java.lang.String);
+ method public final void writeNativeHandle(android.os.NativeHandle);
+ method public final void writeNativeHandleVector(java.util.ArrayList<android.os.NativeHandle>);
+ method public final void writeStatus(int);
+ method public final void writeString(java.lang.String);
+ method public final void writeStringVector(java.util.ArrayList<java.lang.String>);
+ method public final void writeStrongBinder(android.os.IHwBinder);
+ field public static final int STATUS_SUCCESS = 0; // 0x0
+ }
+
+ public static abstract class HwParcel.Status implements java.lang.annotation.Annotation {
+ }
+
+ public abstract interface IHwBinder {
+ method public abstract boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long);
+ method public abstract android.os.IHwInterface queryLocalInterface(java.lang.String);
+ method public abstract void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+ method public abstract boolean unlinkToDeath(android.os.IHwBinder.DeathRecipient);
+ }
+
+ public static abstract interface IHwBinder.DeathRecipient {
+ method public abstract void serviceDied(long);
+ }
+
+ public abstract interface IHwInterface {
+ method public abstract android.os.IHwBinder asBinder();
+ }
+
public class IncidentManager {
method public void reportIncident(android.os.IncidentReportArgs);
}
@@ -810,6 +939,18 @@
field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
}
+ public final class NativeHandle implements java.io.Closeable {
+ ctor public NativeHandle();
+ ctor public NativeHandle(java.io.FileDescriptor, boolean);
+ ctor public NativeHandle(java.io.FileDescriptor[], int[], boolean);
+ method public void close() throws java.io.IOException;
+ method public android.os.NativeHandle dup() throws java.io.IOException;
+ method public java.io.FileDescriptor getFileDescriptor();
+ method public java.io.FileDescriptor[] getFileDescriptors();
+ method public int[] getInts();
+ method public boolean hasSingleFileDescriptor();
+ }
+
public final class MessageQueue {
method public int postSyncBarrier();
method public void removeSyncBarrier(int);
@@ -1252,6 +1393,71 @@
}
+package android.service.autofill.augmented {
+
+ public abstract class AugmentedAutofillService extends android.app.Service {
+ ctor public AugmentedAutofillService();
+ method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method protected void dump(java.io.PrintWriter, java.lang.String[]);
+ method public void onFillRequest(android.service.autofill.augmented.FillRequest, android.os.CancellationSignal, android.service.autofill.augmented.FillController, android.service.autofill.augmented.FillCallback);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
+ }
+
+ public final class FillCallback {
+ method public void onSuccess(android.service.autofill.augmented.FillResponse);
+ }
+
+ public final class FillController {
+ method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>);
+ }
+
+ public final class FillRequest {
+ method public android.content.ComponentName getActivityComponent();
+ method public android.view.autofill.AutofillId getFocusedId();
+ method public android.view.autofill.AutofillValue getFocusedValue();
+ method public android.service.autofill.augmented.PresentationParams getPresentationParams();
+ method public int getTaskId();
+ }
+
+ public final class FillResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.augmented.FillResponse> CREATOR;
+ }
+
+ public static final class FillResponse.Builder {
+ ctor public FillResponse.Builder();
+ method public android.service.autofill.augmented.FillResponse build();
+ method public android.service.autofill.augmented.FillResponse.Builder setFillWindow(android.service.autofill.augmented.FillWindow);
+ method public android.service.autofill.augmented.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>);
+ }
+
+ public final class FillWindow implements java.lang.AutoCloseable {
+ ctor public FillWindow();
+ method public void destroy();
+ method public boolean update(android.service.autofill.augmented.PresentationParams.Area, android.view.View, long);
+ field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
+ }
+
+ public abstract class PresentationParams {
+ method public int getFlags();
+ method public android.service.autofill.augmented.PresentationParams.Area getFullArea();
+ method public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
+ field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
+ field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
+ field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
+ field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
+ field public static final int FLAG_HOST_IME = 16; // 0x10
+ field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
+ }
+
+ public static abstract class PresentationParams.Area {
+ method public android.graphics.Rect getBounds();
+ method public android.service.autofill.augmented.PresentationParams.Area getSubArea(android.graphics.Rect);
+ }
+
+}
+
package android.service.notification {
public final class Adjustment implements android.os.Parcelable {
@@ -1853,6 +2059,10 @@
ctor public AutofillId(android.view.autofill.AutofillId, int);
}
+ public final class AutofillManager {
+ field public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 120000; // 0x1d4c0
+ }
+
}
package android.view.inputmethod {
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index ec2decf..b78e942 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -89,6 +89,7 @@
}
}
+// NOLINTNEXTLINE(cert-dcl50-cpp)
void RawPrintVisitor::print(uint16_t value, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
@@ -101,6 +102,7 @@
offset_ += sizeof(uint16_t);
}
+// NOLINTNEXTLINE(cert-dcl50-cpp)
void RawPrintVisitor::print(uint32_t value, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
@@ -113,6 +115,7 @@
offset_ += sizeof(uint32_t);
}
+// NOLINTNEXTLINE(cert-dcl50-cpp)
void RawPrintVisitor::print(const std::string& value, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index 9d37cdb..ad5eae3 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -57,7 +57,7 @@
break;
case Subscription::SubscriberInformationCase::kPerfettoDetails:
if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(),
- rule_id, configKey)) {
+ subscription.id(), rule_id, configKey)) {
ALOGW("Failed to generate perfetto traces.");
}
break;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 860e40d..f9828a2 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -181,6 +181,7 @@
DocsUISearchTypeReported docs_ui_search_type_reported = 120;
DataStallEvent data_stall_event = 121;
RescuePartyResetReported rescue_party_reset_reported = 122;
+ SignedConfigReported signed_config_reported = 123;
}
// Pulled events will start at field 10000.
@@ -3802,7 +3803,7 @@
//bionic/libc/include/netdb.h
//system/netd/resolv/include/netd_resolv/resolv.h
enum ReturnCode {
- EAI_NO_ERR = 0;
+ EAI_NO_ERROR = 0;
EAI_ADDRFAMILY = 1;
EAI_AGAIN = 2;
EAI_BADFLAGS = 3;
@@ -3859,3 +3860,44 @@
// The rescue level of this reset. A value of 0 indicates missing or unknown level information.
optional int32 rescue_level = 1;
}
+
+/**
+ * Logs when signed config is received from an APK, and if that config was applied successfully.
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+ */
+message SignedConfigReported {
+ enum Type {
+ UNKNOWN_TYPE = 0;
+ GLOBAL_SETTINGS = 1;
+ }
+ optional Type type = 1;
+
+ // The final status of the signed config received.
+ enum Status {
+ UNKNOWN_STATUS = 0;
+ APPLIED = 1;
+ BASE64_FAILURE_CONFIG = 2;
+ BASE64_FAILURE_SIGNATURE = 3;
+ SECURITY_EXCEPTION = 4;
+ INVALID_CONFIG = 5;
+ OLD_CONFIG = 6;
+ SIGNATURE_CHECK_FAILED = 7;
+ NOT_APPLICABLE = 8;
+ }
+ optional Status status = 2;
+
+ // The version of the signed config processed.
+ optional int32 version = 3;
+
+ // The package name that the config was extracted from.
+ optional string from_package = 4;
+
+ enum Key {
+ NO_KEY = 0;
+ DEBUG = 1;
+ PRODUCTION = 2;
+ }
+ // Which key was used to verify the config.
+ optional Key verified_with = 5;
+}
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index 42cc543..0c4c330 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -39,6 +39,7 @@
namespace statsd {
bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
+ int64_t subscription_id,
int64_t alert_id,
const ConfigKey& configKey) {
VLOG("Starting trace collection through perfetto");
@@ -48,9 +49,11 @@
return false;
}
- char alertId[20];
- char configId[20];
- char configUid[20];
+ char subscriptionId[25];
+ char alertId[25];
+ char configId[25];
+ char configUid[25];
+ snprintf(subscriptionId, sizeof(subscriptionId), "%" PRId64, subscription_id);
snprintf(alertId, sizeof(alertId), "%" PRId64, alert_id);
snprintf(configId, sizeof(configId), "%" PRId64, configKey.GetId());
snprintf(configUid, sizeof(configUid), "%d", configKey.GetUid());
@@ -94,7 +97,7 @@
execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox",
kDropboxTag, "--alert-id", alertId, "--config-id", configId, "--config-uid",
- configUid, nullptr);
+ configUid, "--subscription-id", subscriptionId, nullptr);
// execl() doesn't return in case of success, if we get here something
// failed.
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
index 1e7f728..ab2c195 100644
--- a/cmds/statsd/src/external/Perfetto.h
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -32,6 +32,7 @@
// This method returns immediately after passing the config and does NOT wait
// for the full duration of the trace.
bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
+ int64_t subscription_id,
int64_t alert_id,
const ConfigKey& configKey);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 347670d..98c5a0fb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1023,7 +1023,9 @@
*
* @return The content capture manager
*/
- @NonNull private ContentCaptureManager getContentCaptureManager() {
+ @Nullable private ContentCaptureManager getContentCaptureManager() {
+ // ContextCapture disabled for system apps
+ if (getApplicationInfo().isSystemApp()) return null;
if (mContentCaptureManager == null) {
mContentCaptureManager = getSystemService(ContentCaptureManager.class);
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index c90e404..1f01e26 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -317,4 +317,7 @@
/** Returns true if the given uid is the app in the foreground. */
public abstract boolean isAppForeground(int uid);
+
+ /** Remove pending backup for the given userId. */
+ public abstract void clearPendingBackup(int userId);
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 94983e1..9bcb36f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2276,6 +2276,16 @@
}
@Override
+ public String[] setDistractingPackageRestrictions(String[] packages, int distractionFlags) {
+ try {
+ return mPM.setDistractingPackageRestrictionsAsUser(packages, distractionFlags,
+ mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras,
String dialogMessage) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 347973e..fb65da1 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -317,7 +317,6 @@
*/
void requestWifiBugReport(in String shareTitle, in String shareDescription);
- void clearPendingBackup();
Intent getIntentForIntentSender(in IIntentSender sender);
// This is not public because you need to be very careful in how you
// manage your activity to make sure it is always the uid you expect.
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 27581fc..a6abe0b 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -179,16 +179,6 @@
public static final String ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE";
/**
- * The name of the requested role.
- * <p>
- * <strong>Type:</strong> String
- *
- * @hide
- */
- @SystemApi
- public static final String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
-
- /**
* The permission required to manage records of role holders in {@link RoleManager} directly.
*
* @hide
@@ -236,7 +226,7 @@
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Intent intent = new Intent(ACTION_REQUEST_ROLE);
intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
- intent.putExtra(EXTRA_REQUEST_ROLE_NAME, roleName);
+ intent.putExtra(Intent.EXTRA_ROLE_NAME, roleName);
return intent;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e37126b..d27cce5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1833,6 +1833,37 @@
"android.intent.action.REVIEW_PERMISSIONS";
/**
+ * Activity action: Launch UI to manage a default app.
+ * <p>
+ * Input: {@link #EXTRA_ROLE_NAME} specifies the role of the default app which will be managed
+ * by the launched UI.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_MANAGE_DEFAULT_APP =
+ "android.intent.action.MANAGE_DEFAULT_APP";
+
+ /**
+ * Intent extra: A role name.
+ * <p>
+ * Type: String
+ * </p>
+ *
+ * @see android.app.role.RoleManager
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
+
+ /**
* Activity action: Launch UI to manage special app accesses.
* <p>
* Input: Nothing.
@@ -2411,6 +2442,25 @@
public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
/**
+ * Broadcast Action: Distracting packages have been changed.
+ * <p>Includes the following extras:
+ * <ul>
+ * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been changed.
+ * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been changed.
+ * <li> {@link #EXTRA_DISTRACTION_RESTRICTIONS} the new restrictions set on these packages.
+ * </ul>
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system. It is only sent to registered receivers.
+ *
+ * @see PackageManager#setDistractingPackageRestrictions(String[], int)
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DISTRACTING_PACKAGES_CHANGED =
+ "android.intent.action.DISTRACTING_PACKAGES_CHANGED";
+
+ /**
* Broadcast Action: Sent to a package that has been suspended by the system. This is sent
* whenever a package is put into a suspended state or any of its app extras change while in the
* suspended state.
@@ -5120,6 +5170,17 @@
"android.intent.extra.changed_uid_list";
/**
+ * An integer denoting a bitwise combination of restrictions set on distracting packages via
+ * {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
+ *
+ * @hide
+ * @see PackageManager.DistractionRestriction
+ * @see PackageManager#setDistractingPackageRestrictions(String[], int)
+ */
+ public static final String EXTRA_DISTRACTION_RESTRICTIONS =
+ "android.intent.extra.distraction_restrictions";
+
+ /**
* @hide
* Magic extra system code can use when binding, to give a label for
* who it is that has bound to a service. This is an integer giving
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index ba7710b..db2b6fd 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -69,6 +69,7 @@
int userId);
boolean hasShortcutHostPermission(String callingPackage);
+ boolean shouldHideFromSuggestions(String packageName, in UserHandle user);
ParceledListSlice getShortcutConfigActivities(
String callingPackage, String packageName, in UserHandle user);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 64a4479b..d5c3b26 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -273,6 +273,9 @@
void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
+ String[] setDistractingPackageRestrictionsAsUser(in String[] packageNames, int restrictionFlags,
+ int userId);
+
String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
in PersistableBundle appExtras, in PersistableBundle launcherExtras,
in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 44e652f..983ea9f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -697,6 +697,26 @@
}
/**
+ * Returns whether a package should be hidden from suggestions to the user. Currently, this
+ * could be done because the package was marked as distracting to the user via
+ * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}.
+ *
+ * @param packageName The package for which to check.
+ * @param user the {@link UserHandle} of the profile.
+ * @return
+ */
+ public boolean shouldHideFromSuggestions(@NonNull String packageName,
+ @NonNull UserHandle user) {
+ Preconditions.checkNotNull(packageName, "packageName");
+ Preconditions.checkNotNull(user, "user");
+ try {
+ return mService.shouldHideFromSuggestions(packageName, user);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns {@link ApplicationInfo} about an application installed for a specific user profile.
*
* @param packageName The package name of the application
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2aeb68d..636a70f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5889,6 +5889,74 @@
public abstract boolean isSignedByExactly(String packageName, KeySet ks);
/**
+ * Flag to denote no restrictions. This should be used to clear any restrictions that may have
+ * been previously set for the package.
+ * @see PackageManager.DistractionRestriction
+ * @hide
+ */
+ @SystemApi
+ public static final int RESTRICTION_NONE = 0x0;
+
+ /**
+ * Flag to denote that a package should be hidden from any suggestions to the user.
+ * @see PackageManager.DistractionRestriction
+ * @hide
+ */
+ @SystemApi
+ public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001;
+
+ /**
+ * Flag to denote that a package's notifications should be hidden.
+ * @see PackageManager.DistractionRestriction
+ * @hide
+ */
+ @SystemApi
+ public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002;
+
+ /**
+ * Restriction flags to set on a package that is considered as distracting to the user.
+ * These should help the user to restrict their usage of these apps.
+ *
+ * @see #setDistractingPackageRestrictions(String[], int)
+ * @hide
+ */
+ @SystemApi
+ @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = {
+ RESTRICTION_NONE,
+ RESTRICTION_HIDE_FROM_SUGGESTIONS,
+ RESTRICTION_HIDE_NOTIFICATIONS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DistractionRestriction {}
+
+ /**
+ * Mark or unmark the given packages as distracting to the user.
+ * These packages can have certain restrictions set that should discourage the user to launch
+ * them often. For example, notifications from such an app can be hidden, or the app can be
+ * removed from launcher suggestions, so the user is able to restrict their use of these apps.
+ *
+ * <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API.
+ *
+ * @param packages Packages to mark as distracting.
+ * @param restrictionFlags Any combination of {@link DistractionRestriction restrictions} to
+ * impose on the given packages. {@link #RESTRICTION_NONE} can be used
+ * to clear any existing restrictions.
+ * @return A list of packages that could not have the {@code restrictionFlags} set. The system
+ * may prevent restricting critical packages to preserve normal device function.
+ *
+ * @see DistractionRestriction
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.SUSPEND_APPS)
+ @NonNull
+ public String[] setDistractingPackageRestrictions(@NonNull String[] packages,
+ @DistractionRestriction int restrictionFlags) {
+ throw new UnsupportedOperationException(
+ "setDistractingPackageRestrictions not implemented");
+ }
+
+ /**
* Puts the package in a suspended state, where attempts at starting activities are denied.
*
* <p>It doesn't remove the data or the actual package file. The application's notifications
@@ -5911,8 +5979,7 @@
* {@link PersistableBundle} objects to be shared with the apps being suspended and the
* launcher to support customization that they might need to handle the suspended state.
*
- * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or
- * {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
+ * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API.
*
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true}, the packages will be suspended, if set to
@@ -5955,7 +6022,7 @@
* <p>When the user tries to launch a suspended app, a system dialog alerting them that the app
* is suspended will be shown instead.
* The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object
- * to this api. This dialog will have a button that starts the
+ * to this API. This dialog will have a button that starts the
* {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an
* activity which handles this action.
*
@@ -5966,7 +6033,7 @@
* {@link PersistableBundle} objects to be shared with the apps being suspended and the
* launcher to support customization that they might need to handle the suspended state.
*
- * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this api.
+ * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API.
*
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true}, the packages will be suspended, if set to
@@ -6005,7 +6072,7 @@
* #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
* SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
* packages to keep the device in a functioning state, e.g. the default dialer.
- * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this api.
+ * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this API.
*
* <p>
* Note that this set of critical packages can change with time, so even though a package name
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 83979e9..83563d0 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -284,6 +284,17 @@
public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId);
/**
+ * Gets any distraction flags set via
+ * {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
+ *
+ * @param packageName
+ * @param userId
+ * @return A bitwise OR of any of the {@link PackageManager.DistractionRestriction}
+ */
+ public abstract @PackageManager.DistractionRestriction int getDistractingPackageRestrictions(
+ String packageName, int userId);
+
+ /**
* Do a straight uid lookup for the given package/application in the given user.
* @see PackageManager#getPackageUidAsUser(String, int, int)
* @return The app's uid, or < 0 if the package was not found in that user
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 74dd08f..249b691 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -54,6 +54,7 @@
public boolean stopped;
public boolean notLaunched;
public boolean hidden; // Is the app restricted by owner / admin
+ public int distractionFlags;
public boolean suspended;
public String suspendingPackage;
public SuspendDialogInfo dialogInfo;
@@ -92,6 +93,7 @@
stopped = o.stopped;
notLaunched = o.notLaunched;
hidden = o.hidden;
+ distractionFlags = o.distractionFlags;
suspended = o.suspended;
suspendingPackage = o.suspendingPackage;
dialogInfo = o.dialogInfo;
@@ -222,6 +224,9 @@
if (hidden != oldState.hidden) {
return false;
}
+ if (distractionFlags != oldState.distractionFlags) {
+ return false;
+ }
if (suspended != oldState.suspended) {
return false;
}
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index bb8c92d..a3395ac 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -158,6 +158,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final int PROTECTION_FLAG_OEM = 0x4000;
/**
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 4a466f3..617125b3 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -66,6 +66,7 @@
private int mMtu;
// in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
private String mTcpBufferSizes;
+ private IpPrefix mNat64Prefix;
private static final int MIN_MTU = 68;
private static final int MIN_MTU_V6 = 1280;
@@ -760,6 +761,32 @@
}
/**
+ * Returns the NAT64 prefix in use on this link, if any.
+ *
+ * @return the NAT64 prefix.
+ * @hide
+ */
+ public @Nullable IpPrefix getNat64Prefix() {
+ return mNat64Prefix;
+ }
+
+ /**
+ * Sets the NAT64 prefix in use on this link.
+ *
+ * Currently, only 96-bit prefixes (i.e., where the 32-bit IPv4 address is at the end of the
+ * 128-bit IPv6 address) are supported.
+ *
+ * @param prefix the NAT64 prefix.
+ * @hide
+ */
+ public void setNat64Prefix(IpPrefix prefix) {
+ if (prefix != null && prefix.getPrefixLength() != 96) {
+ throw new IllegalArgumentException("Only 96-bit prefixes are supported: " + prefix);
+ }
+ mNat64Prefix = prefix; // IpPrefix objects are immutable.
+ }
+
+ /**
* Adds a stacked link.
*
* If there is already a stacked link with the same interface name as link,
@@ -831,6 +858,7 @@
mStackedLinks.clear();
mMtu = 0;
mTcpBufferSizes = null;
+ mNat64Prefix = null;
}
/**
@@ -908,6 +936,11 @@
resultJoiner.add(mHttpProxy.toString());
}
+ if (mNat64Prefix != null) {
+ resultJoiner.add("Nat64Prefix:");
+ resultJoiner.add(mNat64Prefix.toString());
+ }
+
final Collection<LinkProperties> stackedLinksValues = mStackedLinks.values();
if (!stackedLinksValues.isEmpty()) {
final StringJoiner stackedLinksJoiner = new StringJoiner(",", "Stacked: [", "]");
@@ -1295,6 +1328,17 @@
}
/**
+ * Compares this {@code LinkProperties} NAT64 prefix against the target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalNat64Prefix(LinkProperties target) {
+ return Objects.equals(mNat64Prefix, target.mNat64Prefix);
+ }
+
+ /**
* Compares this {@code LinkProperties} instance against the target
* LinkProperties in {@code obj}. Two LinkPropertieses are equal if
* all their fields are equal in values.
@@ -1330,7 +1374,8 @@
&& isIdenticalHttpProxy(target)
&& isIdenticalStackedLinks(target)
&& isIdenticalMtu(target)
- && isIdenticalTcpBufferSizes(target);
+ && isIdenticalTcpBufferSizes(target)
+ && isIdenticalNat64Prefix(target);
}
/**
@@ -1443,7 +1488,8 @@
+ ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
+ (mUsePrivateDns ? 57 : 0)
+ mPcscfs.size() * 67
- + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode());
+ + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
+ + Objects.hash(mNat64Prefix);
}
/**
@@ -1484,6 +1530,8 @@
} else {
dest.writeByte((byte)0);
}
+ dest.writeParcelable(mNat64Prefix, 0);
+
ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values());
dest.writeList(stackedLinks);
}
@@ -1535,6 +1583,7 @@
if (in.readByte() == 1) {
netProp.setHttpProxy(in.readParcelable(null));
}
+ netProp.setNat64Prefix(in.readParcelable(null));
ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
in.readList(stackedLinks, LinkProperties.class.getClassLoader());
for (LinkProperties stackedLink: stackedLinks) {
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 1343d24..b15a4d3 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -114,7 +114,6 @@
}
}
-
// TODO(b/111441001) Connect up with BugreportListener methods.
private final class DumpstateListener extends IDumpstateListener.Stub
implements DeathRecipient {
@@ -130,6 +129,23 @@
}
@Override
+ public void onProgress(int progress) throws RemoteException {
+ // TODO(b/111441001): implement
+ }
+
+ @Override
+ public void onError(int errorCode) throws RemoteException {
+ // TODO(b/111441001): implement
+ }
+
+ @Override
+ public void onFinished(long durationMs, String title, String description)
+ throws RemoteException {
+ // TODO(b/111441001): implement
+ }
+
+ // Old methods; should go away
+ @Override
public void onProgressUpdated(int progress) throws RemoteException {
// TODO(b/111441001): implement
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 3de3494..9e3e83e 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import libcore.util.NativeAllocationRegistry;
@@ -24,6 +25,7 @@
/** @hide */
@SystemApi
+@TestApi
public abstract class HwBinder implements IHwBinder {
private static final String TAG = "HwBinder";
diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java
index 6a5bb1c..0ec63b5 100644
--- a/core/java/android/os/HwBlob.java
+++ b/core/java/android/os/HwBlob.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import libcore.util.NativeAllocationRegistry;
@@ -28,6 +29,7 @@
* @hide
*/
@SystemApi
+@TestApi
public class HwBlob {
private static final String TAG = "HwBlob";
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index 7a51db2..7919a00 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import libcore.util.NativeAllocationRegistry;
@@ -28,6 +29,7 @@
/** @hide */
@SystemApi
+@TestApi
public class HwParcel {
private static final String TAG = "HwParcel";
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index 249eb3a..46fa6ef 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -17,9 +17,11 @@
package android.os;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
/** @hide */
@SystemApi
+@TestApi
public interface IHwBinder {
/**
* Process a hwbinder transaction.
diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java
index f9edd5b..0a5a715 100644
--- a/core/java/android/os/IHwInterface.java
+++ b/core/java/android/os/IHwInterface.java
@@ -17,8 +17,11 @@
package android.os;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
+
/** @hide */
@SystemApi
+@TestApi
public interface IHwInterface {
/**
* @return the binder object that corresponds to this interface.
diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java
index f7ffc37..f13bf5f 100644
--- a/core/java/android/os/NativeHandle.java
+++ b/core/java/android/os/NativeHandle.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.system.ErrnoException;
import android.system.Os;
@@ -32,6 +33,7 @@
* @hide
*/
@SystemApi
+@TestApi
public final class NativeHandle implements Closeable {
// whether this object owns mFds
private boolean mOwn = false;
diff --git a/core/java/android/os/ParcelableException.aidl b/core/java/android/os/ParcelableException.aidl
new file mode 100644
index 0000000..d214922
--- /dev/null
+++ b/core/java/android/os/ParcelableException.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable ParcelableException;
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 0e18b44..7a7bd83 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -31,4 +31,5 @@
void revokeRuntimePermission(String packageName, String permissionName);
void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
boolean countSystem, in RemoteCallback callback);
+ void getPermissionUsages(boolean countSystem, long numMillis, in RemoteCallback callback);
}
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index d7332ae..0865b62 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -18,6 +18,7 @@
import static android.permission.PermissionControllerService.SERVICE_INTERFACE;
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -146,6 +147,20 @@
void onCountPermissionApps(int numApps);
}
+ /**
+ * Callback for delivering the result of {@link #getPermissionUsages}.
+ *
+ * @hide
+ */
+ public interface OnPermissionUsageResultCallback {
+ /**
+ * The result for {@link #getPermissionUsages}.
+ *
+ * @param users The users list.
+ */
+ void onPermissionUsageResult(@NonNull List<RuntimePermissionUsageInfo> users);
+ }
+
private final @NonNull Context mContext;
/**
@@ -264,6 +279,28 @@
}
/**
+ * Count how many apps have used permissions.
+ *
+ * @param countSystem Also count system apps
+ * @param numMillis The number of milliseconds in the past to check for uses
+ * @param executor Executor on which to invoke the callback
+ * @param callback Callback to receive the result
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+ public void getPermissionUsages(boolean countSystem, long numMillis,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnPermissionUsageResultCallback callback) {
+ checkArgumentNonnegative(numMillis);
+ checkNotNull(executor);
+ checkNotNull(callback);
+
+ sRemoteService.scheduleRequest(new PendingGetPermissionUsagesRequest(sRemoteService,
+ countSystem, numMillis, executor, callback));
+ }
+
+ /**
* A connection to the remote service
*/
static final class RemoteService extends
@@ -326,6 +363,7 @@
private final boolean mDoDryRun;
private final int mReason;
private final @NonNull String mCallingPackage;
+ private final @NonNull Executor mExecutor;
private final @NonNull OnRevokeRuntimePermissionsCallback mCallback;
private final @NonNull RemoteCallback mRemoteCallback;
@@ -341,6 +379,7 @@
mDoDryRun = doDryRun;
mReason = reason;
mCallingPackage = callingPackage;
+ mExecutor = executor;
mCallback = callback;
mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
@@ -375,7 +414,13 @@
@Override
protected void onTimeout(RemoteService remoteService) {
- mCallback.onRevokeRuntimePermissions(Collections.emptyMap());
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallback.onRevokeRuntimePermissions(Collections.emptyMap()));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
@@ -521,4 +566,61 @@
}
}
}
+
+ /**
+ * Request for {@link #getPermissionUsages}
+ */
+ private static final class PendingGetPermissionUsagesRequest extends
+ AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+ private final @NonNull OnPermissionUsageResultCallback mCallback;
+ private final boolean mCountSystem;
+ private final long mNumMillis;
+
+ private final @NonNull RemoteCallback mRemoteCallback;
+
+ private PendingGetPermissionUsagesRequest(@NonNull RemoteService service,
+ boolean countSystem, long numMillis, @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnPermissionUsageResultCallback callback) {
+ super(service);
+
+ mCountSystem = countSystem;
+ mNumMillis = numMillis;
+ mCallback = callback;
+
+ mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
+ long token = Binder.clearCallingIdentity();
+ try {
+ final List<RuntimePermissionUsageInfo> reportedUsers;
+ List<RuntimePermissionUsageInfo> users = null;
+ if (result != null) {
+ users = result.getParcelableArrayList(KEY_RESULT);
+ } else {
+ users = Collections.emptyList();
+ }
+ reportedUsers = users;
+
+ callback.onPermissionUsageResult(reportedUsers);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+
+ finish();
+ }
+ }), null);
+ }
+
+ @Override
+ protected void onTimeout(RemoteService remoteService) {
+ mCallback.onPermissionUsageResult(Collections.emptyList());
+ }
+
+ @Override
+ public void run() {
+ try {
+ getService().getServiceInterface().getPermissionUsages(mCountSystem, mNumMillis,
+ mRemoteCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error counting permission users", e);
+ }
+ }
+ }
}
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index f621737..75d61e6 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -17,6 +17,7 @@
package android.permission;
import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -112,6 +113,17 @@
public abstract int onCountPermissionApps(@NonNull List<String> permissionNames,
boolean countOnlyGranted, boolean countSystem);
+ /**
+ * Count how many apps have used permissions.
+ *
+ * @param countSystem Also count system apps
+ * @param numMillis The number of milliseconds in the past to check for uses
+ *
+ * @return descriptions of the users of permissions
+ */
+ public abstract @NonNull List<RuntimePermissionUsageInfo>
+ onPermissionUsageResult(boolean countSystem, long numMillis);
+
@Override
public final IBinder onBind(Intent intent) {
return new IPermissionController.Stub() {
@@ -187,6 +199,20 @@
PermissionControllerService.this, permissionNames, countOnlyGranted,
countSystem, callback));
}
+
+ @Override
+ public void getPermissionUsages(boolean countSystem, long numMillis,
+ RemoteCallback callback) {
+ checkArgumentNonnegative(numMillis);
+ checkNotNull(callback, "callback");
+
+ enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
+ mHandler.sendMessage(
+ obtainMessage(PermissionControllerService::getPermissionUsages,
+ PermissionControllerService.this, countSystem, numMillis,
+ callback));
+ }
};
}
@@ -230,4 +256,17 @@
result.putInt(PermissionControllerManager.KEY_RESULT, numApps);
callback.sendResult(result);
}
+
+ private void getPermissionUsages(boolean countSystem, long numMillis,
+ @NonNull RemoteCallback callback) {
+ List<RuntimePermissionUsageInfo> users =
+ onPermissionUsageResult(countSystem, numMillis);
+ if (users != null && !users.isEmpty()) {
+ Bundle result = new Bundle();
+ result.putParcelableList(PermissionControllerManager.KEY_RESULT, users);
+ callback.sendResult(result);
+ } else {
+ callback.sendResult(null);
+ }
+ }
}
diff --git a/core/java/android/permission/RuntimePermissionUsageInfo.aidl b/core/java/android/permission/RuntimePermissionUsageInfo.aidl
new file mode 100644
index 0000000..88820dd
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionUsageInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+parcelable RuntimePermissionUsageInfo;
\ No newline at end of file
diff --git a/core/java/android/permission/RuntimePermissionUsageInfo.java b/core/java/android/permission/RuntimePermissionUsageInfo.java
new file mode 100644
index 0000000..af1a1be
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionUsageInfo.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information about how a runtime permission
+ * is used. A single runtime permission presented to the user may
+ * correspond to multiple platform defined permissions, e.g. the
+ * location permission may control both the coarse and fine platform
+ * permissions.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RuntimePermissionUsageInfo implements Parcelable {
+ private final @NonNull CharSequence mName;
+ private final int mNumUsers;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param name The permission group name.
+ * @param numUsers The number of apps that have used this permission.
+ */
+ public RuntimePermissionUsageInfo(@NonNull CharSequence name, int numUsers) {
+ checkNotNull(name);
+ checkArgumentNonnegative(numUsers);
+
+ mName = name;
+ mNumUsers = numUsers;
+ }
+
+ private RuntimePermissionUsageInfo(Parcel parcel) {
+ this(parcel.readCharSequence(), parcel.readInt());
+ }
+
+ /**
+ * @return The number of apps that accessed this permission
+ */
+ public int getAppAccessCount() {
+ return mNumUsers;
+ }
+
+ /**
+ * Gets the permission group name.
+ *
+ * @return The name.
+ */
+ public @NonNull CharSequence getName() {
+ return mName;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeCharSequence(mName);
+ parcel.writeInt(mNumUsers);
+ }
+
+ public static final Creator<RuntimePermissionUsageInfo> CREATOR =
+ new Creator<RuntimePermissionUsageInfo>() {
+ public RuntimePermissionUsageInfo createFromParcel(Parcel source) {
+ return new RuntimePermissionUsageInfo(source);
+ }
+
+ public RuntimePermissionUsageInfo[] newArray(int size) {
+ return new RuntimePermissionUsageInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index a9f4034..0116961 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
@@ -58,6 +59,9 @@
* @hide
*/
@SystemApi
+@TestApi
+// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+// in the same package as the test, and that module is compiled with SDK=test_current
public abstract class AugmentedAutofillService extends Service {
private static final String TAG = AugmentedAutofillService.class.getSimpleName();
@@ -268,7 +272,6 @@
*
* <p>Used to make sure the SmartSuggestionsParams is updated when a new fields is focused.
*/
- // TODO(b/111330312): might not be needed when using IME
@GuardedBy("mLock")
private AutofillId mLastShownId;
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 620ec59..bfb4aad 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.util.Log;
@@ -29,6 +30,9 @@
* @hide
*/
@SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
public final class FillCallback {
private static final String TAG = FillCallback.class.getSimpleName();
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index e65cf47..d7bc893 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.RemoteException;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.util.Log;
@@ -36,6 +37,9 @@
* @hide
*/
@SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
public final class FillController {
private static final String TAG = "FillController";
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index 57d2cc8..dad5067 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.view.autofill.AutofillId;
@@ -29,6 +30,9 @@
*/
@SystemApi
// TODO(b/111330312): pass a requestId and/or sessionId
+@TestApi
+// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+// in the same package as the test, and that module is compiled with SDK=test_current
public final class FillRequest {
final AutofillProxy mProxy;
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index 1ecfab4..5285132 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.autofill.AutofillId;
@@ -30,6 +31,9 @@
* @hide
*/
@SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
public final class FillResponse implements Parcelable {
private final FillWindow mFillWindow;
@@ -50,6 +54,9 @@
* @hide
*/
@SystemApi
+ @TestApi
+ //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+ //in the same package as the test, and that module is compiled with SDK=test_current
public static final class Builder {
private FillWindow mFillWindow;
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index bad7ddd..33b88e42 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -20,6 +20,7 @@
import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Dialog;
import android.graphics.Rect;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
@@ -61,6 +62,9 @@
* @hide
*/
@SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
public final class FillWindow implements AutoCloseable {
private static final String TAG = "FillWindow";
diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
index 0124ecc..b60064e 100644
--- a/core/java/android/service/autofill/augmented/PresentationParams.java
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.graphics.Rect;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.util.DebugUtils;
@@ -48,6 +49,9 @@
* @hide
*/
@SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
public abstract class PresentationParams {
/**
@@ -147,8 +151,11 @@
* Area associated with a {@link PresentationParams Smart Suggestions} provider.
*
* @hide
- * */
+ */
@SystemApi
+ @TestApi
+ //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+ //in the same package as the test, and that module is compiled with SDK=test_current
public abstract static class Area {
/** @hide */
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index cb1d89c..9c935af 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -15,6 +15,7 @@
*/
package android.view.autofill;
+import android.annotation.NonNull;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,33 +26,47 @@
*/
public final class AutofillId implements Parcelable {
+ /** @hide */
+ public static final int NO_SESSION = 0;
+
+ private static final int FLAG_IS_VIRTUAL = 0x1;
+ private static final int FLAG_HAS_SESSION = 0x2;
+
private final int mViewId;
- private final boolean mVirtual;
+ private final int mFlags;
private final int mVirtualId;
+ private final int mSessionId;
/** @hide */
@TestApi
public AutofillId(int id) {
- mVirtual = false;
- mViewId = id;
- mVirtualId = View.NO_ID;
+ this(/* flags= */ 0, id, View.NO_ID, NO_SESSION);
}
/** @hide */
@TestApi
- public AutofillId(AutofillId parent, int virtualChildId) {
- mVirtual = true;
- mViewId = parent.mViewId;
- mVirtualId = virtualChildId;
+ public AutofillId(@NonNull AutofillId parent, int virtualChildId) {
+ this(FLAG_IS_VIRTUAL, parent.mViewId, virtualChildId, NO_SESSION);
}
/** @hide */
public AutofillId(int parentId, int virtualChildId) {
- mVirtual = true;
+ this(FLAG_IS_VIRTUAL, parentId, virtualChildId, NO_SESSION);
+ }
+
+ /** @hide */
+ public AutofillId(@NonNull AutofillId parent, int virtualChildId, int sessionId) {
+ this(FLAG_IS_VIRTUAL | FLAG_HAS_SESSION, parent.mViewId, virtualChildId, sessionId);
+ }
+
+ private AutofillId(int flags, int parentId, int virtualChildId, int sessionId) {
+ mFlags = flags;
mViewId = parentId;
mVirtualId = virtualChildId;
+ mSessionId = sessionId;
}
+
/** @hide */
public int getViewId() {
return mViewId;
@@ -64,7 +79,16 @@
/** @hide */
public boolean isVirtual() {
- return mVirtual;
+ return (mFlags & FLAG_IS_VIRTUAL) != 0;
+ }
+
+ private boolean hasSession() {
+ return (mFlags & FLAG_HAS_SESSION) != 0;
+ }
+
+ /** @hide */
+ public int getSessionId() {
+ return mSessionId;
}
/////////////////////////////////
@@ -77,6 +101,7 @@
int result = 1;
result = prime * result + mViewId;
result = prime * result + mVirtualId;
+ result = prime * result + mSessionId;
return result;
}
@@ -88,15 +113,19 @@
final AutofillId other = (AutofillId) obj;
if (mViewId != other.mViewId) return false;
if (mVirtualId != other.mVirtualId) return false;
+ if (mSessionId != other.mSessionId) return false;
return true;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder().append(mViewId);
- if (mVirtual) {
+ if (isVirtual()) {
builder.append(':').append(mVirtualId);
}
+ if (hasSession()) {
+ builder.append('@').append(mSessionId);
+ }
return builder.toString();
}
@@ -108,21 +137,24 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mViewId);
- parcel.writeInt(mVirtual ? 1 : 0);
- parcel.writeInt(mVirtualId);
- }
-
- private AutofillId(Parcel parcel) {
- mViewId = parcel.readInt();
- mVirtual = parcel.readInt() == 1;
- mVirtualId = parcel.readInt();
+ parcel.writeInt(mFlags);
+ if (isVirtual()) {
+ parcel.writeInt(mVirtualId);
+ }
+ if (hasSession()) {
+ parcel.writeInt(mSessionId);
+ }
}
public static final Parcelable.Creator<AutofillId> CREATOR =
new Parcelable.Creator<AutofillId>() {
@Override
public AutofillId createFromParcel(Parcel source) {
- return new AutofillId(source);
+ final int viewId = source.readInt();
+ final int flags = source.readInt();
+ final int virtualId = (flags & FLAG_IS_VIRTUAL) != 0 ? source.readInt() : View.NO_ID;
+ final int sessionId = (flags & FLAG_HAS_SESSION) != 0 ? source.readInt() : NO_SESSION;
+ return new AutofillId(flags, viewId, virtualId, sessionId);
}
@Override
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 90ccc25..93941d0 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,6 +26,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -329,6 +330,12 @@
private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
/**
+ * @hide
+ */
+ @TestApi
+ public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+ /**
* Makes an authentication id from a request id and a dataset id.
*
* @param requestId The request id.
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 6890beaf..d9a8416 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -28,6 +28,7 @@
import android.view.contentcapture.ViewNode.ViewStructureImpl;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
@@ -107,7 +108,7 @@
/** @hide */
@Nullable
- protected final String mId = UUID.randomUUID().toString();
+ protected final String mId;
private int mState = STATE_UNKNOWN;
@@ -123,6 +124,13 @@
/** @hide */
protected ContentCaptureSession() {
+ this(UUID.randomUUID().toString());
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public ContentCaptureSession(@NonNull String id) {
+ mId = Preconditions.checkNotNull(id);
mCloseGuard.open("destroy");
}
@@ -140,6 +148,13 @@
return mContentCaptureSessionId;
}
+ /** @hide */
+ @VisibleForTesting
+ public int getIdAsInt() {
+ // TODO(b/121197119): use sessionId instead of hashcode once it's changed to int
+ return mId.hashCode();
+ }
+
/**
* Creates a new {@link ContentCaptureSession}.
*
@@ -315,9 +330,7 @@
public @NonNull AutofillId newAutofillId(@NonNull AutofillId parentId, int virtualChildId) {
Preconditions.checkNotNull(parentId);
Preconditions.checkArgument(!parentId.isVirtual(), "virtual ids cannot have children");
- // TODO(b/121197119): we need to add the session id to the AutofillId to make them unique
- // per session
- return new AutofillId(parentId, virtualChildId);
+ return new AutofillId(parentId, virtualChildId, getIdAsInt());
}
/**
@@ -333,8 +346,7 @@
@NonNull
public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId,
int virtualId) {
- // TODO(b/121197119): use the constructor that takes a session id / assert on unit test.
- return new ViewNode.ViewStructureImpl(parentId, virtualId);
+ return new ViewNode.ViewStructureImpl(parentId, virtualId, getIdAsInt());
}
boolean isContentCaptureEnabled() {
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index b7a486a..ddfecb0 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -672,9 +672,9 @@
}
@VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
- public ViewStructureImpl(@NonNull AutofillId parentId, int virtualId) {
+ public ViewStructureImpl(@NonNull AutofillId parentId, int virtualId, int sessionId) {
mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
- mNode.mAutofillId = new AutofillId(parentId, virtualId);
+ mNode.mAutofillId = new AutofillId(parentId, virtualId, sessionId);
}
@VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
diff --git a/core/java/android/view/textclassifier/ConversationAction.java b/core/java/android/view/textclassifier/ConversationAction.java
new file mode 100644
index 0000000..1a6e5d8
--- /dev/null
+++ b/core/java/android/view/textclassifier/ConversationAction.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.textclassifier;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.app.RemoteAction;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+
+/** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
+public final class ConversationAction implements Parcelable {
+
+ /** @hide */
+ @Retention(SOURCE)
+ @StringDef(
+ value = {
+ TYPE_VIEW_CALENDAR,
+ TYPE_VIEW_MAP,
+ TYPE_TRACK_FLIGHT,
+ TYPE_OPEN_URL,
+ TYPE_SEND_SMS,
+ TYPE_CALL_PHONE,
+ TYPE_SEND_EMAIL,
+ TYPE_TEXT_REPLY,
+ TYPE_CREATE_REMINDER,
+ TYPE_SHARE_LOCATION
+ },
+ prefix = "TYPE_")
+ public @interface ActionType {}
+
+ /**
+ * Indicates an action to view a calendar at a specified time.
+ */
+ public static final String TYPE_VIEW_CALENDAR = "view_calendar";
+ /**
+ * Indicates an action to view the map at a specified location.
+ */
+ public static final String TYPE_VIEW_MAP = "view_map";
+ /**
+ * Indicates an action to track a flight.
+ */
+ public static final String TYPE_TRACK_FLIGHT = "track_flight";
+ /**
+ * Indicates an action to open an URL.
+ */
+ public static final String TYPE_OPEN_URL = "open_url";
+ /**
+ * Indicates an action to send a SMS.
+ */
+ public static final String TYPE_SEND_SMS = "send_sms";
+ /**
+ * Indicates an action to call a phone number.
+ */
+ public static final String TYPE_CALL_PHONE = "call_phone";
+ /**
+ * Indicates an action to send an email.
+ */
+ public static final String TYPE_SEND_EMAIL = "send_email";
+ /**
+ * Indicates an action to reply with a text message.
+ */
+ public static final String TYPE_TEXT_REPLY = "text_reply";
+ /**
+ * Indicates an action to create a reminder.
+ */
+ public static final String TYPE_CREATE_REMINDER = "create_reminder";
+ /**
+ * Indicates an action to reply with a location.
+ */
+ public static final String TYPE_SHARE_LOCATION = "share_location";
+
+ public static final Creator<ConversationAction> CREATOR =
+ new Creator<ConversationAction>() {
+ @Override
+ public ConversationAction createFromParcel(Parcel in) {
+ return new ConversationAction(in);
+ }
+
+ @Override
+ public ConversationAction[] newArray(int size) {
+ return new ConversationAction[size];
+ }
+ };
+
+ @NonNull
+ @ActionType
+ private final String mType;
+ @NonNull
+ private final CharSequence mTextReply;
+ @Nullable
+ private final RemoteAction mAction;
+
+ @FloatRange(from = 0, to = 1)
+ private final float mScore;
+
+ @NonNull
+ private final Bundle mExtras;
+
+ private ConversationAction(
+ @NonNull String type,
+ @Nullable RemoteAction action,
+ @Nullable CharSequence textReply,
+ float score,
+ @NonNull Bundle extras) {
+ mType = Preconditions.checkNotNull(type);
+ mAction = action;
+ mTextReply = textReply;
+ mScore = score;
+ mExtras = Preconditions.checkNotNull(extras);
+ }
+
+ private ConversationAction(Parcel in) {
+ mType = in.readString();
+ mAction = in.readParcelable(null);
+ mTextReply = in.readCharSequence();
+ mScore = in.readFloat();
+ mExtras = in.readBundle();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mType);
+ parcel.writeParcelable(mAction, flags);
+ parcel.writeCharSequence(mTextReply);
+ parcel.writeFloat(mScore);
+ parcel.writeBundle(mExtras);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */
+ @NonNull
+ @ActionType
+ public String getType() {
+ return mType;
+ }
+
+ /**
+ * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for
+ * the specified action type.
+ */
+ @Nullable
+ public RemoteAction getAction() {
+ return mAction;
+ }
+
+ /**
+ * Returns the confidence score for the specified action. The value ranges from 0 (low
+ * confidence) to 1 (high confidence).
+ */
+ @FloatRange(from = 0, to = 1)
+ public float getConfidenceScore() {
+ return mScore;
+ }
+
+ /**
+ * Returns the text reply that could be sent as a reply to the given conversation.
+ * <p>
+ * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}.
+ */
+ @Nullable
+ public CharSequence getTextReply() {
+ return mTextReply;
+ }
+
+ /**
+ * Returns the extended data related to this conversation action.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras.deepCopy();
+ }
+
+ /** Builder class to construct {@link ConversationAction}. */
+ public static final class Builder {
+ @Nullable
+ @ActionType
+ private String mType;
+ @Nullable
+ private RemoteAction mAction;
+ @Nullable
+ private CharSequence mTextReply;
+ private float mScore;
+ @Nullable
+ private Bundle mExtras;
+
+ public Builder(@NonNull @ActionType String actionType) {
+ mType = Preconditions.checkNotNull(actionType);
+ }
+
+ /**
+ * Sets an action that may be performed on the given conversation.
+ */
+ @NonNull
+ public Builder setAction(@Nullable RemoteAction action) {
+ mAction = action;
+ return this;
+ }
+
+ /**
+ * Sets a text reply that may be performed on the given conversation.
+ */
+ @NonNull
+ public Builder setTextReply(@Nullable CharSequence textReply) {
+ mTextReply = textReply;
+ return this;
+ }
+
+ /** Sets the confident score. */
+ @NonNull
+ public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) {
+ mScore = score;
+ return this;
+ }
+
+ /**
+ * Sets the extended data for the conversation action object.
+ */
+ @NonNull
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /** Builds the {@link ConversationAction} object. */
+ @NonNull
+ public ConversationAction build() {
+ return new ConversationAction(
+ mType,
+ mAction,
+ mTextReply,
+ mScore,
+ mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
+ }
+ }
+}
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 3f690f7..f7c1a26 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -17,18 +17,15 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.app.Person;
-import android.app.RemoteAction;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.SpannedString;
-import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
@@ -37,10 +34,8 @@
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.Set;
/**
* Represents a list of actions suggested by a {@link TextClassifier} on a given conversation.
@@ -62,83 +57,6 @@
}
};
- /** @hide */
- @Retention(SOURCE)
- @StringDef(
- value = {
- TYPE_VIEW_CALENDAR,
- TYPE_VIEW_MAP,
- TYPE_TRACK_FLIGHT,
- TYPE_OPEN_URL,
- TYPE_SEND_SMS,
- TYPE_CALL_PHONE,
- TYPE_SEND_EMAIL,
- TYPE_TEXT_REPLY,
- TYPE_CREATE_REMINDER,
- TYPE_SHARE_LOCATION
- },
- prefix = "TYPE_")
- public @interface ActionType {}
-
- /**
- * Indicates an action to view a calendar at a specified time.
- */
- public static final String TYPE_VIEW_CALENDAR = "view_calendar";
- /**
- * Indicates an action to view the map at a specified location.
- */
- public static final String TYPE_VIEW_MAP = "view_map";
- /**
- * Indicates an action to track a flight.
- */
- public static final String TYPE_TRACK_FLIGHT = "track_flight";
- /**
- * Indicates an action to open an URL.
- */
- public static final String TYPE_OPEN_URL = "open_url";
- /**
- * Indicates an action to send a SMS.
- */
- public static final String TYPE_SEND_SMS = "send_sms";
- /**
- * Indicates an action to call a phone number.
- */
- public static final String TYPE_CALL_PHONE = "call_phone";
- /**
- * Indicates an action to send an email.
- */
- public static final String TYPE_SEND_EMAIL = "send_email";
- /**
- * Indicates an action to reply with a text message.
- */
- public static final String TYPE_TEXT_REPLY = "text_reply";
- /**
- * Indicates an action to create a reminder.
- */
- public static final String TYPE_CREATE_REMINDER = "create_reminder";
- /**
- * Indicates an action to reply with a location.
- */
- public static final String TYPE_SHARE_LOCATION = "share_location";
-
- /** @hide */
- @Retention(SOURCE)
- @StringDef(
- value = {
- HINT_FOR_NOTIFICATION,
- HINT_FOR_IN_APP,
- },
- prefix = "HINT_")
- public @interface Hint {}
- /**
- * To indicate the generated actions will be used within the app.
- */
- public static final String HINT_FOR_IN_APP = "in_app";
- /**
- * To indicate the generated actions will be used for notification.
- */
- public static final String HINT_FOR_NOTIFICATION = "notification";
-
private final List<ConversationAction> mConversationActions;
private final String mId;
@@ -184,182 +102,6 @@
return mId;
}
- /** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
- public static final class ConversationAction implements Parcelable {
-
- public static final Creator<ConversationAction> CREATOR =
- new Creator<ConversationAction>() {
- @Override
- public ConversationAction createFromParcel(Parcel in) {
- return new ConversationAction(in);
- }
-
- @Override
- public ConversationAction[] newArray(int size) {
- return new ConversationAction[size];
- }
- };
-
- @NonNull
- @ActionType
- private final String mType;
- @NonNull
- private final CharSequence mTextReply;
- @Nullable
- private final RemoteAction mAction;
-
- @FloatRange(from = 0, to = 1)
- private final float mScore;
-
- @NonNull
- private final Bundle mExtras;
-
- private ConversationAction(
- @NonNull String type,
- @Nullable RemoteAction action,
- @Nullable CharSequence textReply,
- float score,
- @NonNull Bundle extras) {
- mType = Preconditions.checkNotNull(type);
- mAction = action;
- mTextReply = textReply;
- mScore = score;
- mExtras = Preconditions.checkNotNull(extras);
- }
-
- private ConversationAction(Parcel in) {
- mType = in.readString();
- mAction = in.readParcelable(null);
- mTextReply = in.readCharSequence();
- mScore = in.readFloat();
- mExtras = in.readBundle();
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeString(mType);
- parcel.writeParcelable(mAction, flags);
- parcel.writeCharSequence(mTextReply);
- parcel.writeFloat(mScore);
- parcel.writeBundle(mExtras);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- @ActionType
- /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */
- public String getType() {
- return mType;
- }
-
- @Nullable
- /**
- * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for
- * the specified action type.
- */
- public RemoteAction getAction() {
- return mAction;
- }
-
- /**
- * Returns the confidence score for the specified action. The value ranges from 0 (low
- * confidence) to 1 (high confidence).
- */
- @FloatRange(from = 0, to = 1)
- public float getConfidenceScore() {
- return mScore;
- }
-
- /**
- * Returns the text reply that could be sent as a reply to the given conversation.
- * <p>
- * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}.
- */
- @Nullable
- public CharSequence getTextReply() {
- return mTextReply;
- }
-
- /**
- * Returns the extended data related to this conversation action.
- *
- * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
- * prefer to hold a reference to the returned bundle rather than frequently calling this
- * method.
- */
- @NonNull
- public Bundle getExtras() {
- return mExtras.deepCopy();
- }
-
- /** Builder class to construct {@link ConversationAction}. */
- public static final class Builder {
- @Nullable
- @ActionType
- private String mType;
- @Nullable
- private RemoteAction mAction;
- @Nullable
- private CharSequence mTextReply;
- private float mScore;
- @Nullable
- private Bundle mExtras;
-
- public Builder(@NonNull @ActionType String actionType) {
- mType = Preconditions.checkNotNull(actionType);
- }
-
- /**
- * Sets an action that may be performed on the given conversation.
- */
- @NonNull
- public Builder setAction(@Nullable RemoteAction action) {
- mAction = action;
- return this;
- }
-
- /**
- * Sets a text reply that may be performed on the given conversation.
- */
- @NonNull
- public Builder setTextReply(@Nullable CharSequence textReply) {
- mTextReply = textReply;
- return this;
- }
-
- /** Sets the confident score. */
- @NonNull
- public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) {
- mScore = score;
- return this;
- }
-
- /**
- * Sets the extended data for the conversation action object.
- */
- @NonNull
- public Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /** Builds the {@link ConversationAction} object. */
- @NonNull
- public ConversationAction build() {
- return new ConversationAction(
- mType,
- mAction,
- mTextReply,
- mScore,
- mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
- }
- }
- }
-
/** Represents a message in the conversation. */
public static final class Message implements Parcelable {
/**
@@ -538,156 +280,36 @@
}
}
- /** Configuration object for specifying what action types to identify. */
- public static final class TypeConfig implements Parcelable {
- @NonNull
- @ActionType
- private final Set<String> mExcludedTypes;
- @NonNull
- @ActionType
- private final Set<String> mIncludedTypes;
- private final boolean mIncludeTypesFromTextClassifier;
-
- private TypeConfig(
- @NonNull Set<String> includedTypes,
- @NonNull Set<String> excludedTypes,
- boolean includeTypesFromTextClassifier) {
- mIncludedTypes = Preconditions.checkNotNull(includedTypes);
- mExcludedTypes = Preconditions.checkNotNull(excludedTypes);
- mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
- }
-
- private TypeConfig(Parcel in) {
- mIncludedTypes = new ArraySet<>(in.createStringArrayList());
- mExcludedTypes = new ArraySet<>(in.createStringArrayList());
- mIncludeTypesFromTextClassifier = in.readByte() != 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeStringList(new ArrayList<>(mIncludedTypes));
- parcel.writeStringList(new ArrayList<>(mExcludedTypes));
- parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0));
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final Creator<TypeConfig> CREATOR =
- new Creator<TypeConfig>() {
- @Override
- public TypeConfig createFromParcel(Parcel in) {
- return new TypeConfig(in);
- }
-
- @Override
- public TypeConfig[] newArray(int size) {
- return new TypeConfig[size];
- }
- };
-
- /**
- * Returns a final list of types that the text classifier should look for.
- *
- * <p>NOTE: This method is intended for use by a text classifier.
- *
- * @param defaultTypes types the text classifier thinks should be included before factoring
- * in the included/excluded types given by the client.
- */
- @NonNull
- public Collection<String> resolveTypes(@Nullable Collection<String> defaultTypes) {
- Set<String> types = new ArraySet<>();
- if (mIncludeTypesFromTextClassifier && defaultTypes != null) {
- types.addAll(defaultTypes);
- }
- types.addAll(mIncludedTypes);
- types.removeAll(mExcludedTypes);
- return Collections.unmodifiableCollection(types);
- }
-
- /**
- * Return whether the client allows the text classifier to include its own list of default
- * types. If this function returns {@code true}, the text classifier can consider specifying
- * a default list of entity types in {@link #resolveTypes(Collection)}.
- *
- * <p>NOTE: This method is intended for use by a text classifier.
- *
- * @see #resolveTypes(Collection)
- */
- public boolean shouldIncludeTypesFromTextClassifier() {
- return mIncludeTypesFromTextClassifier;
- }
-
- /** Builder class to construct the {@link TypeConfig} object. */
- public static final class Builder {
- @Nullable
- private Collection<String> mExcludedTypes;
- @Nullable
- private Collection<String> mIncludedTypes;
- private boolean mIncludeTypesFromTextClassifier = true;
-
- /**
- * Sets a collection of types that are explicitly included, for example, {@link
- * #TYPE_VIEW_CALENDAR}.
- */
- @NonNull
- public Builder setIncludedTypes(
- @Nullable @ActionType Collection<String> includedTypes) {
- mIncludedTypes = includedTypes;
- return this;
- }
-
- /**
- * Sets a collection of types that are explicitly excluded, for example, {@link
- * #TYPE_VIEW_CALENDAR}.
- */
- @NonNull
- public Builder setExcludedTypes(
- @Nullable @ActionType Collection<String> excludedTypes) {
- mExcludedTypes = excludedTypes;
- return this;
- }
-
- /**
- * Specifies whether or not to include the types suggested by the text classifier. By
- * default, it is included.
- */
- @NonNull
- public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) {
- mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
- return this;
- }
-
- /**
- * Combines all of the options that have been set and returns a new {@link TypeConfig}
- * object.
- */
- @NonNull
- public TypeConfig build() {
- return new TypeConfig(
- mIncludedTypes == null
- ? Collections.emptySet()
- : new ArraySet<>(mIncludedTypes),
- mExcludedTypes == null
- ? Collections.emptySet()
- : new ArraySet<>(mExcludedTypes),
- mIncludeTypesFromTextClassifier);
- }
- }
- }
-
/**
* A request object for generating conversation action suggestions.
*
* @see TextClassifier#suggestConversationActions(Request)
*/
public static final class Request implements Parcelable {
+
+ /** @hide */
+ @Retention(SOURCE)
+ @StringDef(
+ value = {
+ HINT_FOR_NOTIFICATION,
+ HINT_FOR_IN_APP,
+ },
+ prefix = "HINT_")
+ public @interface Hint {}
+
+ /**
+ * To indicate the generated actions will be used within the app.
+ */
+ public static final String HINT_FOR_IN_APP = "in_app";
+ /**
+ * To indicate the generated actions will be used for notification.
+ */
+ public static final String HINT_FOR_NOTIFICATION = "notification";
+
@NonNull
private final List<Message> mConversation;
@NonNull
- private final TypeConfig mTypeConfig;
+ private final TextClassifier.EntityConfig mTypeConfig;
private final int mMaxSuggestions;
@NonNull
@Hint
@@ -699,7 +321,7 @@
private Request(
@NonNull List<Message> conversation,
- @NonNull TypeConfig typeConfig,
+ @NonNull TextClassifier.EntityConfig typeConfig,
int maxSuggestions,
String conversationId,
@Nullable @Hint List<String> hints) {
@@ -713,7 +335,7 @@
private static Request readFromParcel(Parcel in) {
List<Message> conversation = new ArrayList<>();
in.readParcelableList(conversation, null);
- TypeConfig typeConfig = in.readParcelable(null);
+ TextClassifier.EntityConfig typeConfig = in.readParcelable(null);
int maxSuggestions = in.readInt();
String conversationId = in.readString();
List<String> hints = new ArrayList<>();
@@ -760,7 +382,7 @@
/** Returns the type config. */
@NonNull
- public TypeConfig getTypeConfig() {
+ public TextClassifier.EntityConfig getTypeConfig() {
return mTypeConfig;
}
@@ -820,7 +442,7 @@
@NonNull
private List<Message> mConversation;
@Nullable
- private TypeConfig mTypeConfig;
+ private TextClassifier.EntityConfig mTypeConfig;
private int mMaxSuggestions;
@Nullable
private String mConversationId;
@@ -849,7 +471,7 @@
/** Sets the type config. */
@NonNull
- public Builder setTypeConfig(@Nullable TypeConfig typeConfig) {
+ public Builder setTypeConfig(@Nullable TextClassifier.EntityConfig typeConfig) {
mTypeConfig = typeConfig;
return this;
}
@@ -879,7 +501,9 @@
public Request build() {
return new Request(
Collections.unmodifiableList(mConversation),
- mTypeConfig == null ? new TypeConfig.Builder().build() : mTypeConfig,
+ mTypeConfig == null
+ ? new TextClassifier.EntityConfig.Builder().build()
+ : mTypeConfig,
mMaxSuggestions,
mConversationId,
mHints == null
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 50801a2..ce680ec 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -117,15 +117,15 @@
.add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES =
new StringJoiner(ENTITY_LIST_DELIMITER)
- .add(ConversationActions.TYPE_TEXT_REPLY)
- .add(ConversationActions.TYPE_CREATE_REMINDER)
- .add(ConversationActions.TYPE_CALL_PHONE)
- .add(ConversationActions.TYPE_OPEN_URL)
- .add(ConversationActions.TYPE_SEND_EMAIL)
- .add(ConversationActions.TYPE_SEND_SMS)
- .add(ConversationActions.TYPE_TRACK_FLIGHT)
- .add(ConversationActions.TYPE_VIEW_CALENDAR)
- .add(ConversationActions.TYPE_VIEW_MAP)
+ .add(ConversationAction.TYPE_TEXT_REPLY)
+ .add(ConversationAction.TYPE_CREATE_REMINDER)
+ .add(ConversationAction.TYPE_CALL_PHONE)
+ .add(ConversationAction.TYPE_OPEN_URL)
+ .add(ConversationAction.TYPE_SEND_EMAIL)
+ .add(ConversationAction.TYPE_SEND_SMS)
+ .add(ConversationAction.TYPE_TRACK_FLIGHT)
+ .add(ConversationAction.TYPE_VIEW_CALENDAR)
+ .add(ConversationAction.TYPE_VIEW_MAP)
.toString();
private final boolean mSystemTextClassifierEnabled;
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 8709e09..5a56136 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -32,7 +32,6 @@
import android.text.util.Linkify;
import android.text.util.Linkify.LinkifyMask;
import android.util.ArrayMap;
-import android.util.ArraySet;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -43,6 +42,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -324,7 +324,7 @@
}
/**
- * Detects the language of the specified text.
+ * Detects the language of the text in the given request.
*
* <p><strong>NOTE: </strong>Call on a worker thread.
*
@@ -403,42 +403,59 @@
default void dump(@NonNull IndentingPrintWriter printWriter) {}
/**
- * Configuration object for specifying what entities to identify.
+ * Configuration object for specifying what entity types to identify.
*
* Configs are initially based on a predefined preset, and can be modified from there.
*/
final class EntityConfig implements Parcelable {
- private final Collection<String> mHints;
- private final Collection<String> mExcludedEntityTypes;
- private final Collection<String> mIncludedEntityTypes;
- private final boolean mUseHints;
+ private final List<String> mIncludedTypes;
+ private final List<String> mExcludedTypes;
+ private final List<String> mHints;
+ private final boolean mIncludeTypesFromTextClassifier;
- private EntityConfig(boolean useHints, Collection<String> hints,
- Collection<String> includedEntityTypes, Collection<String> excludedEntityTypes) {
- mHints = hints == null
- ? Collections.EMPTY_LIST
- : Collections.unmodifiableCollection(new ArraySet<>(hints));
- mExcludedEntityTypes = excludedEntityTypes == null
- ? Collections.EMPTY_LIST : new ArraySet<>(excludedEntityTypes);
- mIncludedEntityTypes = includedEntityTypes == null
- ? Collections.EMPTY_LIST : new ArraySet<>(includedEntityTypes);
- mUseHints = useHints;
+ private EntityConfig(
+ List<String> includedEntityTypes,
+ List<String> excludedEntityTypes,
+ List<String> hints,
+ boolean includeTypesFromTextClassifier) {
+ mIncludedTypes = Preconditions.checkNotNull(includedEntityTypes);
+ mExcludedTypes = Preconditions.checkNotNull(excludedEntityTypes);
+ mHints = Preconditions.checkNotNull(hints);
+ mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+ }
+
+ private EntityConfig(Parcel in) {
+ mIncludedTypes = new ArrayList<>();
+ in.readStringList(mIncludedTypes);
+ mExcludedTypes = new ArrayList<>();
+ in.readStringList(mExcludedTypes);
+ List<String> tmpHints = new ArrayList<>();
+ in.readStringList(tmpHints);
+ mHints = Collections.unmodifiableList(tmpHints);
+ mIncludeTypesFromTextClassifier = in.readByte() != 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeStringList(mIncludedTypes);
+ parcel.writeStringList(mExcludedTypes);
+ parcel.writeStringList(mHints);
+ parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0));
}
/**
* Creates an EntityConfig.
*
* @param hints Hints for the TextClassifier to determine what types of entities to find.
+ *
+ * @deprecated Use {@link Builder} instead.
*/
+ @Deprecated
public static EntityConfig createWithHints(@Nullable Collection<String> hints) {
- return new EntityConfig(/* useHints */ true, hints,
- /* includedEntityTypes */null, /* excludedEntityTypes */ null);
- }
-
- // TODO: Remove once apps can build against the latest sdk.
- /** @hide */
- public static EntityConfig create(@Nullable Collection<String> hints) {
- return createWithHints(hints);
+ return new EntityConfig.Builder()
+ .includeTypesFromTextClassifier(true)
+ .setHints(hints)
+ .build();
}
/**
@@ -450,12 +467,19 @@
*
*
* Note that if an entity has been excluded, the exclusion will take precedence.
+ *
+ * @deprecated Use {@link Builder} instead.
*/
+ @Deprecated
public static EntityConfig create(@Nullable Collection<String> hints,
@Nullable Collection<String> includedEntityTypes,
@Nullable Collection<String> excludedEntityTypes) {
- return new EntityConfig(/* useHints */ true, hints,
- includedEntityTypes, excludedEntityTypes);
+ return new EntityConfig.Builder()
+ .setIncludedTypes(includedEntityTypes)
+ .setExcludedTypes(excludedEntityTypes)
+ .setHints(hints)
+ .includeTypesFromTextClassifier(true)
+ .build();
}
/**
@@ -463,34 +487,33 @@
*
* @param entityTypes Complete set of entities, e.g. {@link #TYPE_URL} to find.
*
+ * @deprecated Use {@link Builder} instead.
*/
+ @Deprecated
public static EntityConfig createWithExplicitEntityList(
@Nullable Collection<String> entityTypes) {
- return new EntityConfig(/* useHints */ false, /* hints */ null,
- /* includedEntityTypes */ entityTypes, /* excludedEntityTypes */ null);
- }
-
- // TODO: Remove once apps can build against the latest sdk.
- /** @hide */
- public static EntityConfig createWithEntityList(@Nullable Collection<String> entityTypes) {
- return createWithExplicitEntityList(entityTypes);
+ return new EntityConfig.Builder()
+ .setIncludedTypes(entityTypes)
+ .includeTypesFromTextClassifier(false)
+ .build();
}
/**
- * Returns a list of the final set of entities to find.
+ * Returns a final list of entity types to find.
*
- * @param entities Entities we think should be found before factoring in includes/excludes
+ * @param entityTypes Entity types we think should be found before factoring in
+ * includes/excludes
*
* This method is intended for use by TextClassifier implementations.
*/
public Collection<String> resolveEntityListModifications(
- @NonNull Collection<String> entities) {
- final Set<String> finalSet = new HashSet();
- if (mUseHints) {
- finalSet.addAll(entities);
+ @NonNull Collection<String> entityTypes) {
+ final Set<String> finalSet = new HashSet<>();
+ if (mIncludeTypesFromTextClassifier) {
+ finalSet.addAll(entityTypes);
}
- finalSet.addAll(mIncludedEntityTypes);
- finalSet.removeAll(mExcludedEntityTypes);
+ finalSet.addAll(mIncludedTypes);
+ finalSet.removeAll(mExcludedTypes);
return finalSet;
}
@@ -503,17 +526,22 @@
return mHints;
}
- @Override
- public int describeContents() {
- return 0;
+ /**
+ * Return whether the client allows the text classifier to include its own list of
+ * default types. If this function returns {@code true}, a default list of types suggested
+ * from a text classifier will be taking into account.
+ *
+ * <p>NOTE: This method is intended for use by a text classifier.
+ *
+ * @see #resolveEntityListModifications(Collection)
+ */
+ public boolean shouldIncludeTypesFromTextClassifier() {
+ return mIncludeTypesFromTextClassifier;
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeStringList(new ArrayList<>(mHints));
- dest.writeStringList(new ArrayList<>(mExcludedEntityTypes));
- dest.writeStringList(new ArrayList<>(mIncludedEntityTypes));
- dest.writeInt(mUseHints ? 1 : 0);
+ public int describeContents() {
+ return 0;
}
public static final Parcelable.Creator<EntityConfig> CREATOR =
@@ -529,11 +557,75 @@
}
};
- private EntityConfig(Parcel in) {
- mHints = new ArraySet<>(in.createStringArrayList());
- mExcludedEntityTypes = new ArraySet<>(in.createStringArrayList());
- mIncludedEntityTypes = new ArraySet<>(in.createStringArrayList());
- mUseHints = in.readInt() == 1;
+
+
+ /** Builder class to construct the {@link EntityConfig} object. */
+ public static final class Builder {
+ @Nullable
+ private Collection<String> mIncludedTypes;
+ @Nullable
+ private Collection<String> mExcludedTypes;
+ @Nullable
+ private Collection<String> mHints;
+ private boolean mIncludeTypesFromTextClassifier = true;
+
+ /**
+ * Sets a collection of types that are explicitly included.
+ */
+ @NonNull
+ public Builder setIncludedTypes(@Nullable Collection<String> includedTypes) {
+ mIncludedTypes = includedTypes;
+ return this;
+ }
+
+ /**
+ * Sets a collection of types that are explicitly excluded.
+ */
+ @NonNull
+ public Builder setExcludedTypes(@Nullable Collection<String> excludedTypes) {
+ mExcludedTypes = excludedTypes;
+ return this;
+ }
+
+ /**
+ * Specifies whether or not to include the types suggested by the text classifier. By
+ * default, it is included.
+ */
+ @NonNull
+ public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) {
+ mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+ return this;
+ }
+
+
+ /**
+ * Sets the hints for the TextClassifier to determine what types of entities to find.
+ * These hints will only be used if {@link #includeTypesFromTextClassifier} is
+ * set to be true.
+ */
+ public Builder setHints(Collection<String> hints) {
+ mHints = hints;
+ return this;
+ }
+
+ /**
+ * Combines all of the options that have been set and returns a new {@link EntityConfig}
+ * object.
+ */
+ @NonNull
+ public EntityConfig build() {
+ return new EntityConfig(
+ mIncludedTypes == null
+ ? Collections.emptyList()
+ : new ArrayList<>(mIncludedTypes),
+ mExcludedTypes == null
+ ? Collections.emptyList()
+ : new ArrayList<>(mExcludedTypes),
+ mHints == null
+ ? Collections.emptyList()
+ : Collections.unmodifiableList(new ArrayList<>(mHints)),
+ mIncludeTypesFromTextClassifier);
+ }
}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index d5b9eb1..9ab963e 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -393,7 +393,7 @@
actionsImpl.suggestActions(nativeConversation, null);
Collection<String> expectedTypes = resolveActionTypesFromRequest(request);
- List<ConversationActions.ConversationAction> conversationActions = new ArrayList<>();
+ List<ConversationAction> conversationActions = new ArrayList<>();
int maxSuggestions = nativeSuggestions.length;
if (request.getMaxSuggestions() > 0) {
maxSuggestions = Math.min(request.getMaxSuggestions(), nativeSuggestions.length);
@@ -405,7 +405,7 @@
continue;
}
conversationActions.add(
- new ConversationActions.ConversationAction.Builder(actionType)
+ new ConversationAction.Builder(actionType)
.setTextReply(nativeSuggestion.getResponseText())
.setConfidenceScore(nativeSuggestion.getScore())
.build());
@@ -445,10 +445,10 @@
private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
List<String> defaultActionTypes =
- request.getHints().contains(ConversationActions.HINT_FOR_NOTIFICATION)
+ request.getHints().contains(ConversationActions.Request.HINT_FOR_NOTIFICATION)
? mSettings.getNotificationConversationActionTypes()
: mSettings.getInAppConversationActionTypes();
- return request.getTypeConfig().resolveTypes(defaultActionTypes);
+ return request.getTypeConfig().resolveEntityListModifications(defaultActionTypes);
}
private AnnotatorModel getAnnotatorImpl(LocaleList localeList)
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 9b9b771..8e88c51 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -23,6 +23,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.service.procstats.ProcessStatsAvailablePagesProto;
import android.service.procstats.ProcessStatsPackageProto;
import android.service.procstats.ProcessStatsSectionProto;
import android.text.format.DateFormat;
@@ -178,7 +179,7 @@
{"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 35;
+ private static final int PARCEL_VERSION = 36;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -237,10 +238,11 @@
ArrayList<String> mIndexToCommonString;
private static final Pattern sPageTypeRegex = Pattern.compile(
- "^Node\\s+(\\d+),.*. type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$");
- private final ArrayList<Integer> mPageTypeZones = new ArrayList<Integer>();
- private final ArrayList<String> mPageTypeLabels = new ArrayList<String>();
- private final ArrayList<int[]> mPageTypeSizes = new ArrayList<int[]>();
+ "^Node\\s+(\\d+),.* zone\\s+(\\w+),.* type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$");
+ private final ArrayList<Integer> mPageTypeNodes = new ArrayList<>();
+ private final ArrayList<String> mPageTypeZones = new ArrayList<>();
+ private final ArrayList<String> mPageTypeLabels = new ArrayList<>();
+ private final ArrayList<int[]> mPageTypeSizes = new ArrayList<>();
public ProcessStats(boolean running) {
mRunning = running;
@@ -621,6 +623,7 @@
try {
reader = new BufferedReader(new FileReader("/proc/pagetypeinfo"));
final Matcher matcher = sPageTypeRegex.matcher("");
+ mPageTypeNodes.clear();
mPageTypeZones.clear();
mPageTypeLabels.clear();
mPageTypeSizes.clear();
@@ -631,16 +634,18 @@
}
matcher.reset(line);
if (matcher.matches()) {
- final Integer zone = Integer.valueOf(matcher.group(1), 10);
- if (zone == null) {
+ final Integer node = Integer.valueOf(matcher.group(1), 10);
+ if (node == null) {
continue;
}
- mPageTypeZones.add(zone);
- mPageTypeLabels.add(matcher.group(2));
- mPageTypeSizes.add(splitAndParseNumbers(matcher.group(3)));
+ mPageTypeNodes.add(node);
+ mPageTypeZones.add(matcher.group(2));
+ mPageTypeLabels.add(matcher.group(3));
+ mPageTypeSizes.add(splitAndParseNumbers(matcher.group(4)));
}
}
} catch (IOException ex) {
+ mPageTypeNodes.clear();
mPageTypeZones.clear();
mPageTypeLabels.clear();
mPageTypeSizes.clear();
@@ -935,7 +940,8 @@
final int NPAGETYPES = mPageTypeLabels.size();
out.writeInt(NPAGETYPES);
for (int i=0; i<NPAGETYPES; i++) {
- out.writeInt(mPageTypeZones.get(i));
+ out.writeInt(mPageTypeNodes.get(i));
+ out.writeString(mPageTypeZones.get(i));
out.writeString(mPageTypeLabels.get(i));
out.writeIntArray(mPageTypeSizes.get(i));
}
@@ -1244,6 +1250,8 @@
// Fragmentation info
final int NPAGETYPES = in.readInt();
+ mPageTypeNodes.clear();
+ mPageTypeNodes.ensureCapacity(NPAGETYPES);
mPageTypeZones.clear();
mPageTypeZones.ensureCapacity(NPAGETYPES);
mPageTypeLabels.clear();
@@ -1251,7 +1259,8 @@
mPageTypeSizes.clear();
mPageTypeSizes.ensureCapacity(NPAGETYPES);
for (int i=0; i<NPAGETYPES; i++) {
- mPageTypeZones.add(in.readInt());
+ mPageTypeNodes.add(in.readInt());
+ mPageTypeZones.add(in.readString());
mPageTypeLabels.add(in.readString());
mPageTypeSizes.add(in.createIntArray());
}
@@ -1764,7 +1773,8 @@
pw.println("Available pages by page size:");
final int NPAGETYPES = mPageTypeLabels.size();
for (int i=0; i<NPAGETYPES; i++) {
- pw.format("Zone %3d %14s ", mPageTypeZones.get(i), mPageTypeLabels.get(i));
+ pw.format("Node %3d Zone %7s %14s ", mPageTypeNodes.get(i), mPageTypeZones.get(i),
+ mPageTypeLabels.get(i));
final int[] sizes = mPageTypeSizes.get(i);
final int N = sizes == null ? 0 : sizes.length;
for (int j=0; j<N; j++) {
@@ -2095,6 +2105,9 @@
pw.print(",");
pw.print(mPageTypeZones.get(i));
pw.print(",");
+ // Wasn't included in original output.
+ //pw.print(mPageTypeNodes.get(i));
+ //pw.print(",");
final int[] sizes = mPageTypeSizes.get(i);
final int N = sizes == null ? 0 : sizes.length;
for (int j=0; j<N; j++) {
@@ -2135,6 +2148,20 @@
proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL);
}
+ final int NPAGETYPES = mPageTypeLabels.size();
+ for (int i = 0; i < NPAGETYPES; i++) {
+ final long token = proto.start(ProcessStatsSectionProto.AVAILABLE_PAGES);
+ proto.write(ProcessStatsAvailablePagesProto.NODE, mPageTypeNodes.get(i));
+ proto.write(ProcessStatsAvailablePagesProto.ZONE, mPageTypeZones.get(i));
+ proto.write(ProcessStatsAvailablePagesProto.LABEL, mPageTypeLabels.get(i));
+ final int[] sizes = mPageTypeSizes.get(i);
+ final int N = sizes == null ? 0 : sizes.length;
+ for (int j = 0; j < N; j++) {
+ proto.write(ProcessStatsAvailablePagesProto.PAGES_PER_ORDER, sizes[j]);
+ }
+ proto.end(token);
+ }
+
final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
if ((section & REPORT_PROC_STATS) != 0) {
for (int ip = 0; ip < procMap.size(); ip++) {
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index c1e0047..92aae27 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -715,7 +715,7 @@
#endif
// Dup the file descriptor so we can keep a reference to it after the Parcel
// is disposed.
- int dupFd = dup(blob.fd());
+ int dupFd = fcntl(blob.fd(), F_DUPFD_CLOEXEC, 0);
if (dupFd < 0) {
ALOGE("Error allocating dup fd. Error:%d", errno);
blob.release();
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 7ef06dc..3b59321 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -473,7 +473,7 @@
if (parcel != NULL) {
int fd = parcel->readFileDescriptor();
if (fd < 0) return NULL;
- fd = dup(fd);
+ fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
if (fd < 0) return NULL;
return jniCreateFileDescriptor(env, fd);
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 3e1c5a3..2f2f623 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -29,6 +29,12 @@
// ACTION: Settings > Any preference is changed
ACTION_SETTINGS_PREFERENCE_CHANGE = 853;
+
+ // ACTION: Tap & Pay -> Default Application Setting -> Use Forground
+ ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622;
+
+ // ACTION: Tap & Pay -> Default Application Setting -> Use Default
+ ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623;
}
/**
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index 4ecf52c..7f96d70 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -111,6 +111,7 @@
optional EnabledState enabled_state = 7;
optional string last_disabled_app_caller = 8;
optional string suspending_package = 9;
+ optional int32 distraction_flags = 10;
}
// Name of package. e.g. "com.android.providers.telephony".
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index 71ebcc1..da801ff 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -43,7 +43,7 @@
* Data model from /frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
* This proto is defined based on the writeToParcel method.
*
- * Next Tag: 10
+ * Next Tag: 11
*/
message ProcessStatsSectionProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -76,6 +76,9 @@
}
repeated Status status = 7;
+ // Number of pages available of various types and sizes, representation fragmentation.
+ repeated ProcessStatsAvailablePagesProto available_pages = 10;
+
// Stats for each process.
repeated ProcessStatsProto process_stats = 8;
@@ -83,6 +86,24 @@
repeated ProcessStatsPackageProto package_stats = 9;
}
+// Next Tag: 5
+message ProcessStatsAvailablePagesProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Node these pages are in (as per /proc/pagetypeinfo)
+ optional int32 node = 1;
+
+ // Zone these pages are in (as per /proc/pagetypeinfo)
+ optional string zone = 2;
+
+ // Label for the type of these pages (as per /proc/pagetypeinfo)
+ optional string label = 3;
+
+ // Distribution of number of pages available by order size. First entry in array is
+ // order 0, second is order 1, etc. Each order increase is a doubling of page size.
+ repeated int32 pages_per_order = 4;
+}
+
// Next Tag: 10
message ProcessStatsStateProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8147b4a..2f3a491 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -51,6 +51,7 @@
<protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" />
<protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" />
<protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" />
+ <protected-broadcast android:name="android.intent.action.DISTRACTING_PACKAGES_CHANGED" />
<protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" />
<protected-broadcast android:name="android.intent.action.UID_REMOVED" />
<protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
@@ -2268,6 +2269,11 @@
<permission android:name="android.permission.START_ANY_ACTIVITY"
android:protectionLevel="signature" />
+ <!-- Allows an application to start activities from background
+ @hide -->
+ <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
+ android:protectionLevel="signature|privileged|vendorPrivileged|oem" />
+
<!-- @SystemApi Must be required by activities that handle the intent action
{@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that
hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system.
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
new file mode 100644
index 0000000..7619af2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.autofill;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.os.Parcel;
+import android.view.View;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AutofillIdTest {
+
+ @Test
+ public void testNonVirtual() {
+ final AutofillId id = new AutofillId(42);
+ assertThat(id.getViewId()).isEqualTo(42);
+ assertThat(id.isVirtual()).isFalse();
+ assertThat(id.getVirtualChildId()).isEqualTo(View.NO_ID);
+
+ final AutofillId clone = cloneThroughParcel(id);
+ assertThat(clone.getViewId()).isEqualTo(42);
+ assertThat(clone.isVirtual()).isFalse();
+ assertThat(clone.getVirtualChildId()).isEqualTo(View.NO_ID);
+ }
+
+ @Test
+ public void testVirtual() {
+ final AutofillId id = new AutofillId(42, 108);
+ assertThat(id.getViewId()).isEqualTo(42);
+ assertThat(id.isVirtual()).isTrue();
+ assertThat(id.getVirtualChildId()).isEqualTo(108);
+
+ final AutofillId clone = cloneThroughParcel(id);
+ assertThat(clone.getViewId()).isEqualTo(42);
+ assertThat(clone.isVirtual()).isTrue();
+ assertThat(clone.getVirtualChildId()).isEqualTo(108);
+ }
+
+ @Test
+ public void testVirtual_parentObjectConstructor() {
+ assertThrows(NullPointerException.class, () -> new AutofillId(null, 108));
+
+ final AutofillId id = new AutofillId(new AutofillId(42), 108);
+ assertThat(id.getViewId()).isEqualTo(42);
+ assertThat(id.isVirtual()).isTrue();
+ assertThat(id.getVirtualChildId()).isEqualTo(108);
+
+ final AutofillId clone = cloneThroughParcel(id);
+ assertThat(clone.getViewId()).isEqualTo(42);
+ assertThat(clone.isVirtual()).isTrue();
+ assertThat(clone.getVirtualChildId()).isEqualTo(108);
+ }
+
+ @Test
+ public void testVirtual_withSession() {
+ final AutofillId id = new AutofillId(new AutofillId(42), 108, 666);
+ assertThat(id.getViewId()).isEqualTo(42);
+ assertThat(id.isVirtual()).isTrue();
+ assertThat(id.getVirtualChildId()).isEqualTo(108);
+ assertThat(id.getSessionId()).isEqualTo(666);
+
+ final AutofillId clone = cloneThroughParcel(id);
+ assertThat(clone.getViewId()).isEqualTo(42);
+ assertThat(clone.isVirtual()).isTrue();
+ assertThat(clone.getVirtualChildId()).isEqualTo(108);
+ assertThat(clone.getSessionId()).isEqualTo(666);
+ }
+
+ @Test
+ public void testEqualsHashCode() {
+ final AutofillId realId = new AutofillId(42);
+ final AutofillId realIdSame = new AutofillId(42);
+ assertThat(realId).isEqualTo(realIdSame);
+ assertThat(realIdSame).isEqualTo(realId);
+ assertThat(realId.hashCode()).isEqualTo(realIdSame.hashCode());
+
+ final AutofillId realIdDifferent = new AutofillId(108);
+ assertThat(realId).isNotEqualTo(realIdDifferent);
+ assertThat(realIdDifferent).isNotEqualTo(realId);
+
+ final AutofillId virtualId = new AutofillId(42, 1);
+ final AutofillId virtualIdSame = new AutofillId(42, 1);
+ assertThat(virtualId).isEqualTo(virtualIdSame);
+ assertThat(virtualIdSame).isEqualTo(virtualId);
+ assertThat(virtualId.hashCode()).isEqualTo(virtualIdSame.hashCode());
+ assertThat(virtualId).isNotEqualTo(realId);
+ assertThat(realId).isNotEqualTo(virtualId);
+
+ final AutofillId virtualIdDifferentChild = new AutofillId(42, 2);
+ assertThat(virtualIdDifferentChild).isNotEqualTo(virtualId);
+ assertThat(virtualId).isNotEqualTo(virtualIdDifferentChild);
+ assertThat(virtualIdDifferentChild).isNotEqualTo(realId);
+ assertThat(realId).isNotEqualTo(virtualIdDifferentChild);
+
+ final AutofillId virtualIdDifferentParent = new AutofillId(108, 1);
+ assertThat(virtualIdDifferentParent).isNotEqualTo(virtualId);
+ assertThat(virtualId).isNotEqualTo(virtualIdDifferentParent);
+ assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild);
+ assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent);
+
+ final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108);
+ assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId);
+ assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession);
+ assertThat(virtualIdDifferentSession).isNotEqualTo(realId);
+ assertThat(realId).isNotEqualTo(virtualIdDifferentSession);
+
+ final AutofillId sameVirtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108);
+ assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession);
+ assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession);
+ assertThat(sameVirtualIdDifferentSession.hashCode())
+ .isEqualTo(virtualIdDifferentSession.hashCode());
+ }
+
+ private AutofillId cloneThroughParcel(AutofillId id) {
+ Parcel parcel = Parcel.obtain();
+
+ try {
+ // Write to parcel
+ parcel.setDataPosition(0); // Sanity / paranoid check
+ id.writeToParcel(parcel, 0);
+
+ // Read from parcel
+ parcel.setDataPosition(0);
+ AutofillId clone = AutofillId.CREATOR.createFromParcel(parcel);
+ assertThat(clone).isNotNull();
+ return clone;
+ } finally {
+ parcel.recycle();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 59f3a4c..73cceae 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -19,11 +19,14 @@
import static org.testng.Assert.assertThrows;
+import android.view.View;
+import android.view.ViewStructure;
import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Spy;
+import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
/**
@@ -35,34 +38,104 @@
@RunWith(MockitoJUnitRunner.class)
public class ContentCaptureSessionTest {
- /**
- * Uses a spy as ContentCaptureSession is abstract but (so far) we're testing its concrete
- * methods.
- */
- @Spy
- private ContentCaptureSession mMockSession;
+ private ContentCaptureSession mSession1 = new MyContentCaptureSession("111");
+
+ private ContentCaptureSession mSession2 = new MyContentCaptureSession("2222");
+
+ @Mock
+ private View mMockView;
@Test
public void testNewAutofillId_invalid() {
- assertThrows(NullPointerException.class, () -> mMockSession.newAutofillId(null, 42));
+ assertThrows(NullPointerException.class, () -> mSession1.newAutofillId(null, 42));
assertThrows(IllegalArgumentException.class,
- () -> mMockSession.newAutofillId(new AutofillId(42, 42), 42));
+ () -> mSession1.newAutofillId(new AutofillId(42, 42), 42));
}
@Test
public void testNewAutofillId_valid() {
final AutofillId parentId = new AutofillId(42);
- final AutofillId childId = mMockSession.newAutofillId(parentId, 108);
+ final AutofillId childId = mSession1.newAutofillId(parentId, 108);
assertThat(childId.getViewId()).isEqualTo(42);
assertThat(childId.getVirtualChildId()).isEqualTo(108);
- // TODO(b/121197119): assert session id
+ assertThat(childId.getSessionId()).isEqualTo(mSession1.getIdAsInt());
+ }
+
+ @Test
+ public void testNewAutofillId_differentSessions() {
+ assertThat(mSession1.getIdAsInt()).isNotSameAs(mSession2.getIdAsInt()); //sanity check
+ final AutofillId parentId = new AutofillId(42);
+ final AutofillId childId1 = mSession1.newAutofillId(parentId, 108);
+ final AutofillId childId2 = mSession2.newAutofillId(parentId, 108);
+ assertThat(childId1).isNotEqualTo(childId2);
+ assertThat(childId2).isNotEqualTo(childId1);
}
@Test
public void testNotifyXXX_null() {
- assertThrows(NullPointerException.class, () -> mMockSession.notifyViewAppeared(null));
- assertThrows(NullPointerException.class, () -> mMockSession.notifyViewDisappeared(null));
+ assertThrows(NullPointerException.class, () -> mSession1.notifyViewAppeared(null));
+ assertThrows(NullPointerException.class, () -> mSession1.notifyViewDisappeared(null));
assertThrows(NullPointerException.class,
- () -> mMockSession.notifyViewTextChanged(null, "whatever", 0));
+ () -> mSession1.notifyViewTextChanged(null, "whatever", 0));
+ }
+
+ @Test
+ public void testNewViewStructure() {
+ assertThat(mMockView.getAutofillId()).isNotNull(); // sanity check
+ final ViewStructure structure = mSession1.newViewStructure(mMockView);
+ assertThat(structure).isNotNull();
+ assertThat(structure.getAutofillId()).isEqualTo(mMockView.getAutofillId());
+ }
+
+ @Test
+ public void testNewVirtualViewStructure() {
+ final AutofillId parentId = new AutofillId(42);
+ final ViewStructure structure = mSession1.newVirtualViewStructure(parentId, 108);
+ assertThat(structure).isNotNull();
+ final AutofillId childId = mSession1.newAutofillId(parentId, 108);
+ assertThat(structure.getAutofillId()).isEqualTo(childId);
+ }
+
+ // Cannot use @Spy because we need to pass the session id on constructor
+ private class MyContentCaptureSession extends ContentCaptureSession {
+
+ private MyContentCaptureSession(String id) {
+ super(id);
+ }
+
+ @Override
+ MainContentCaptureSession getMainCaptureSession() {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ ContentCaptureSession newChild(ContentCaptureContext context) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ void flush() {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ void onDestroy() {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ void internalNotifyViewAppeared(ViewStructureImpl node) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ void internalNotifyViewDisappeared(AutofillId id) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ void internalNotifyViewTextChanged(AutofillId id, CharSequence text, int flags) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
}
}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
index 995946b..bbfe01c 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
@@ -110,10 +110,10 @@
@Test
public void testAutofillIdMethods_explicitIdsConstructor() {
AutofillId initialParentId = new AutofillId(42);
- ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108);
+ ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108, 666);
ViewNode node = structure.getNode();
- assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108));
+ assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108, 666));
assertThat(node.getParentAutofillId()).isEqualTo(initialParentId);
AutofillId newChildId = new AutofillId(108);
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 82eaf88..ec6101c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -377,10 +377,10 @@
ConversationActions.Message.PERSON_USER_REMOTE)
.setText("Where are you?")
.build();
- ConversationActions.TypeConfig typeConfig =
- new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
+ TextClassifier.EntityConfig typeConfig =
+ new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
.setIncludedTypes(
- Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+ Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
.build();
ConversationActions.Request request =
new ConversationActions.Request.Builder(Collections.singletonList(message))
@@ -391,10 +391,10 @@
ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
assertTrue(conversationActions.getConversationActions().size() > 0);
assertTrue(conversationActions.getConversationActions().size() == 1);
- for (ConversationActions.ConversationAction conversationAction :
+ for (ConversationAction conversationAction :
conversationActions.getConversationActions()) {
assertThat(conversationAction,
- isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
+ isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
}
}*/
@@ -406,10 +406,10 @@
ConversationActions.Message.PERSON_USER_REMOTE)
.setText("Where are you?")
.build();
- ConversationActions.TypeConfig typeConfig =
- new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
+ TextClassifier.EntityConfig typeConfig =
+ new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
.setIncludedTypes(
- Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+ Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
.build();
ConversationActions.Request request =
new ConversationActions.Request.Builder(Collections.singletonList(message))
@@ -418,10 +418,10 @@
ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
assertTrue(conversationActions.getConversationActions().size() > 1);
- for (ConversationActions.ConversationAction conversationAction :
+ for (ConversationAction conversationAction :
conversationActions.getConversationActions()) {
assertThat(conversationAction,
- isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
+ isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
}
}
@@ -524,20 +524,19 @@
};
}
- private static Matcher<ConversationActions.ConversationAction> isConversationAction(
- String actionType) {
- return new BaseMatcher<ConversationActions.ConversationAction>() {
+ private static Matcher<ConversationAction> isConversationAction(String actionType) {
+ return new BaseMatcher<ConversationAction>() {
@Override
public boolean matches(Object o) {
- if (!(o instanceof ConversationActions.ConversationAction)) {
+ if (!(o instanceof ConversationAction)) {
return false;
}
- ConversationActions.ConversationAction conversationAction =
- (ConversationActions.ConversationAction) o;
+ ConversationAction conversationAction =
+ (ConversationAction) o;
if (!actionType.equals(conversationAction.getType())) {
return false;
}
- if (ConversationActions.TYPE_TEXT_REPLY.equals(actionType)) {
+ if (ConversationAction.TYPE_TEXT_REPLY.equals(actionType)) {
if (conversationAction.getTextReply() == null) {
return false;
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
index 344f79d..0597a89 100644
--- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
@@ -26,7 +26,7 @@
import android.metrics.LogMaker;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.view.textclassifier.ConversationActions;
+import android.view.textclassifier.ConversationAction;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextClassifierEventTronLogger;
@@ -69,7 +69,7 @@
new TextClassifierEvent.Builder(
TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS,
TextClassifierEvent.TYPE_SMART_ACTION)
- .setEntityType(ConversationActions.TYPE_CALL_PHONE)
+ .setEntityType(ConversationAction.TYPE_CALL_PHONE)
.setEventTime(EVENT_TIME)
.setEventContext(textClassificationContext)
.build();
@@ -84,7 +84,7 @@
assertThat(logMaker.getType()).isEqualTo(
ACTION_TEXT_SELECTION_SMART_SHARE);
assertThat(logMaker.getTaggedData(FIELD_SELECTION_ENTITY_TYPE))
- .isEqualTo(ConversationActions.TYPE_CALL_PHONE);
+ .isEqualTo(ConversationAction.TYPE_CALL_PHONE);
assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME))
.isEqualTo(EVENT_TIME);
assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME);
diff --git a/core/tests/overlaytests/device/Android.mk b/core/tests/overlaytests/device/Android.mk
index 680ebeb..5630749 100644
--- a/core/tests/overlaytests/device/Android.mk
+++ b/core/tests/overlaytests/device/Android.mk
@@ -19,7 +19,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := OverlayDeviceTests
LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_TARGET_REQUIRED_MODULES := \
OverlayDeviceTests_AppOverlayOne \
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index d14fdf5..4881636 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -23,7 +23,7 @@
<uses-library android:name="android.test.runner"/>
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.overlaytest"
android:label="Runtime resource overlay tests" />
</manifest>
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index 5a86885..91fcdbb 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -25,10 +25,11 @@
import android.content.res.XmlResourceParser;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
-import android.support.test.InstrumentationRegistry;
import android.util.AttributeSet;
import android.util.Xml;
+import androidx.test.InstrumentationRegistry;
+
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index f35e511..cd3ed9d 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -16,7 +16,7 @@
package com.android.overlaytest;
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index 037449f..c0d4281 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -16,7 +16,7 @@
package com.android.overlaytest;
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index f657b5c..33c7b25 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -16,7 +16,7 @@
package com.android.overlaytest;
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 25dabad..6ce8148 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -39,6 +39,41 @@
name: "privapp-permissions-platform.xml",
sub_dir: "permissions",
src: "privapp-permissions-platform.xml",
+ required: [
+ "privapp_whitelist_com.android.settings.intelligence",
+ ],
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.carrierconfig",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.carrierconfig.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.contacts",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.contacts.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.launcher3",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.launcher3.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.provision",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.provision.xml",
+ filename_from_src: true,
}
prebuilt_etc {
@@ -50,6 +85,21 @@
}
prebuilt_etc {
+ name: "privapp_whitelist_com.android.settings.intelligence",
+ sub_dir: "permissions",
+ src: "com.android.settings.intelligence.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.storagemanager",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.storagemanager.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_com.android.systemui",
product_specific: true,
sub_dir: "permissions",
diff --git a/data/etc/com.android.carrierconfig.xml b/data/etc/com.android.carrierconfig.xml
new file mode 100644
index 0000000..17efb03
--- /dev/null
+++ b/data/etc/com.android.carrierconfig.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.carrierconfig">
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.contacts.xml b/data/etc/com.android.contacts.xml
new file mode 100644
index 0000000..78eae40
--- /dev/null
+++ b/data/etc/com.android.contacts.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.contacts">
+ <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml
new file mode 100644
index 0000000..337e153
--- /dev/null
+++ b/data/etc/com.android.launcher3.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.launcher3">
+ <permission name="android.permission.BIND_APPWIDGET"/>
+ <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
+ <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.provision.xml b/data/etc/com.android.provision.xml
new file mode 100644
index 0000000..05404ef
--- /dev/null
+++ b/data/etc/com.android.provision.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.provision">
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.settings.intelligence.xml b/data/etc/com.android.settings.intelligence.xml
new file mode 100644
index 0000000..6ca30c1
--- /dev/null
+++ b/data/etc/com.android.settings.intelligence.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.settings.intelligence">
+ <permission name="android.permission.MANAGE_FINGERPRINT"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+ <permission name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.storagemanager.xml b/data/etc/com.android.storagemanager.xml
new file mode 100644
index 0000000..e85a82c
--- /dev/null
+++ b/data/etc/com.android.storagemanager.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.storagemanager">
+ <permission name="android.permission.DELETE_PACKAGES"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+ <permission name="android.permission.USE_RESERVED_DISK"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 221708b..3562a8f 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -44,6 +44,7 @@
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
<permission name="android.permission.START_ACTIVITY_AS_CALLER"/>
<permission name="android.permission.START_TASKS_FROM_RECENTS"/>
<permission name="android.permission.STATUS_BAR"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index fbe613d..597d14a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -33,10 +33,6 @@
<permission name="android.permission.CRYPT_KEEPER"/>
</privapp-permissions>
- <privapp-permissions package="com.android.carrierconfig">
- <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.cellbroadcastreceiver">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
@@ -45,11 +41,6 @@
<permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
</privapp-permissions>
- <privapp-permissions package="com.android.contacts">
- <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
- <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.defcontainer">
<permission name="android.permission.ACCESS_CACHE_FILESYSTEM"/>
<permission name="android.permission.ALLOCATE_AGGRESSIVE"/>
@@ -79,12 +70,6 @@
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
- <privapp-permissions package="com.android.launcher3">
- <permission name="android.permission.BIND_APPWIDGET"/>
- <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
- <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.location.fused">
<permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
</privapp-permissions>
@@ -238,10 +223,6 @@
<permission name="android.permission.USE_RESERVED_DISK"/>
</privapp-permissions>
- <privapp-permissions package="com.android.provision">
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.mainline.networkstack">
<permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/>
<permission name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"/>
@@ -280,13 +261,6 @@
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
</privapp-permissions>
- <privapp-permissions package="com.android.settings.intelligence">
- <permission name="android.permission.MANAGE_FINGERPRINT"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
- <permission name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.sharedstoragebackup">
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
@@ -342,6 +316,7 @@
<permission name="android.permission.SET_TIME"/>
<permission name="android.permission.SET_TIME_ZONE"/>
<permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES"/>
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
<permission name="android.permission.START_TASKS_FROM_RECENTS" />
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
@@ -356,15 +331,6 @@
<permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
</privapp-permissions>
- <privapp-permissions package="com.android.storagemanager">
- <permission name="android.permission.DELETE_PACKAGES"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.PACKAGE_USAGE_STATS"/>
- <permission name="android.permission.USE_RESERVED_DISK"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.tv">
<permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/>
<permission name="android.permission.DVB_DEVICE"/>
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index ca9dc47..65aaba1 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -22,6 +22,7 @@
import android.annotation.Size;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Canvas.VertexMode;
+import android.graphics.text.MeasuredText;
import android.text.GraphicsOperations;
import android.text.MeasuredParagraph;
import android.text.PrecomputedText;
@@ -554,14 +555,12 @@
final int paraStart = pt.getParagraphStart(paraIndex);
final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex);
// Only support the text in the same paragraph.
- nDrawTextRun(mNativeCanvasWrapper,
- mp.getChars(),
- start - paraStart,
- end - start,
- contextStart - paraStart,
- contextEnd - contextStart,
- x, y, isRtl, paint.getNativeInstance(),
- mp.getMeasuredText().getNativePtr());
+ drawTextRun(mp.getMeasuredText(),
+ start - paraStart,
+ end - paraStart,
+ contextStart - paraStart,
+ contextEnd - paraStart,
+ x, y, isRtl, paint);
return;
}
}
@@ -576,6 +575,14 @@
}
}
+ public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end,
+ int contextStart, int contextEnd, float x, float y, boolean isRtl,
+ @NonNull Paint paint) {
+ nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start,
+ contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(),
+ measuredText.getNativePtr());
+ }
+
public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,
int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,
int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount,
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 901c211..4f60935 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.graphics.text.MeasuredText;
import android.text.GraphicsOperations;
import android.text.MeasuredParagraph;
import android.text.PrecomputedText;
@@ -522,14 +523,12 @@
final int paraStart = pt.getParagraphStart(paraIndex);
final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex);
// Only support if the target is in the same paragraph.
- nDrawTextRun(mNativeCanvasWrapper,
- mp.getChars(),
+ drawTextRun(mp.getMeasuredText(),
start - paraStart,
- end - start,
+ end - paraStart,
contextStart - paraStart,
- contextEnd - contextStart,
- x, y, isRtl, paint.getNativeInstance(),
- mp.getMeasuredText().getNativePtr());
+ contextEnd - paraStart,
+ x, y, isRtl, paint);
return;
}
}
@@ -545,6 +544,15 @@
}
@Override
+ public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end,
+ int contextStart, int contextEnd, float x, float y, boolean isRtl,
+ @NonNull Paint paint) {
+ nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start,
+ contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(),
+ measuredText.getNativePtr());
+ }
+
+ @Override
public final void drawVertices(@NonNull VertexMode mode, int vertexCount,
@NonNull float[] verts, int vertOffset, @Nullable float[] texs, int texOffset,
@Nullable int[] colors, int colorOffset, @Nullable short[] indices, int indexOffset,
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 63a806e..8c1bae2 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.UnsupportedAppUsage;
+import android.graphics.text.MeasuredText;
import android.os.Build;
import dalvik.annotation.optimization.CriticalNative;
@@ -2122,7 +2123,8 @@
* the text next to it.
* <p>
* All text outside the range {@code contextStart..contextEnd} is ignored. The text between
- * {@code start} and {@code end} will be laid out and drawn.
+ * {@code start} and {@code end} will be laid out and drawn. The context range is useful for
+ * contextual shaping, e.g. Kerning, Arabic contextural form.
* <p>
* The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is
* suitable only for runs of a single direction. Alignment of the text is as determined by the
@@ -2151,6 +2153,31 @@
}
/**
+ * Draw a run of text, all in a single direction, with optional context for complex text
+ * shaping.
+ * <p>
+ * See {@link #drawTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)} for
+ * more details. This method uses a {@link MeasuredText} rather than CharSequence to represent
+ * the string.
+ *
+ * @param text the text to render
+ * @param start the start of the text to render. Data before this position can be used for
+ * shaping context.
+ * @param end the end of the text to render. Data at or after this position can be used for
+ * shaping context.
+ * @param contextStart the index of the start of the shaping context
+ * @param contextEnd the index of the end of the shaping context
+ * @param x the x position at which to draw the text
+ * @param y the y position at which to draw the text
+ * @param isRtl whether the run is in RTL direction
+ * @param paint the paint
+ */
+ public void drawTextRun(@NonNull MeasuredText text, int start, int end, int contextStart,
+ int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) {
+ super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, isRtl, paint);
+ }
+
+ /**
* Draw the array of vertices, interpreted as triangles (based on mode). The verts array is
* required, and specifies the x,y pairs for each vertex. If texs is non-null, then it is used
* to specify the coordinate in shader coordinates to use at each vertex (the paint must have a
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index 57e3491..3dc1f2c 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -51,7 +51,7 @@
class BagAttributeFinder
: public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> {
public:
- BagAttributeFinder(const ResolvedBag* bag)
+ explicit BagAttributeFinder(const ResolvedBag* bag)
: BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr,
bag != nullptr ? bag->entries + bag->entry_count : nullptr) {
}
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 5694115..a99e77f 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -94,7 +94,7 @@
if (size < 0) {
result = UNKNOWN_ERROR;
} else {
- int dupAshmemFd = ::dup(ashmemFd);
+ int dupAshmemFd = ::fcntl(ashmemFd, F_DUPFD_CLOEXEC, 0);
if (dupAshmemFd < 0) {
result = -errno;
} else {
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 5d243da..5be2105 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -37,7 +37,7 @@
// TODO: This can go away once the only remaining usage in aapt goes away.
class FileReader : public zip_archive::Reader {
public:
- FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
+ explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
}
bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
diff --git a/libs/androidfw/include/androidfw/AssetDir.h b/libs/androidfw/include/androidfw/AssetDir.h
index 7aef02d..ce6e066 100644
--- a/libs/androidfw/include/androidfw/AssetDir.h
+++ b/libs/androidfw/include/androidfw/AssetDir.h
@@ -78,7 +78,7 @@
class FileInfo {
public:
FileInfo(void) {}
- FileInfo(const String8& path) // useful for e.g. svect.indexOf
+ explicit FileInfo(const String8& path) // useful for e.g. svect.indexOf
: mFileName(path), mFileType(kFileTypeUnknown)
{}
~FileInfo(void) {}
diff --git a/libs/androidfw/include/androidfw/BackupHelpers.h b/libs/androidfw/include/androidfw/BackupHelpers.h
index fc1ad47..2da247b 100644
--- a/libs/androidfw/include/androidfw/BackupHelpers.h
+++ b/libs/androidfw/include/androidfw/BackupHelpers.h
@@ -67,7 +67,7 @@
class BackupDataWriter
{
public:
- BackupDataWriter(int fd);
+ explicit BackupDataWriter(int fd);
// does not close fd
~BackupDataWriter();
@@ -104,7 +104,7 @@
class BackupDataReader
{
public:
- BackupDataReader(int fd);
+ explicit BackupDataReader(int fd);
// does not close fd
~BackupDataReader();
diff --git a/libs/androidfw/include/androidfw/ConfigDescription.h b/libs/androidfw/include/androidfw/ConfigDescription.h
index 29424c4..6fa089a 100644
--- a/libs/androidfw/include/androidfw/ConfigDescription.h
+++ b/libs/androidfw/include/androidfw/ConfigDescription.h
@@ -82,7 +82,7 @@
static void ApplyVersionForCompatibility(ConfigDescription* config);
ConfigDescription();
- ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit)
+ ConfigDescription(const android::ResTable_config& o); // NOLINT(google-explicit-constructor)
ConfigDescription(const ConfigDescription& o);
ConfigDescription(ConfigDescription&& o) noexcept;
diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
index e1dfb94..bf35aa3 100644
--- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
+++ b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
@@ -22,7 +22,7 @@
class DisplayEventDispatcher : public LooperCallback {
public:
- DisplayEventDispatcher(const sp<Looper>& looper,
+ explicit DisplayEventDispatcher(const sp<Looper>& looper,
ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp);
status_t initialize();
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index cf2d8fb..9b05d1f 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -693,7 +693,7 @@
class ResXMLParser
{
public:
- ResXMLParser(const ResXMLTree& tree);
+ explicit ResXMLParser(const ResXMLTree& tree);
enum event_code_t {
BAD_DOCUMENT = -1,
@@ -806,7 +806,7 @@
* The tree stores a clone of the specified DynamicRefTable, so any changes to the original
* DynamicRefTable will not affect this tree after instantiation.
**/
- ResXMLTree(const DynamicRefTable* dynamicRefTable);
+ explicit ResXMLTree(const DynamicRefTable* dynamicRefTable);
ResXMLTree();
~ResXMLTree();
@@ -1844,7 +1844,7 @@
class Theme {
public:
- Theme(const ResTable& table);
+ explicit Theme(const ResTable& table);
~Theme();
inline const ResTable& getResTable() const { return mTable; }
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index a33865f..921877dc 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -52,8 +52,8 @@
BasicStringPiece();
BasicStringPiece(const BasicStringPiece<TChar>& str);
- BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(implicit)
- BasicStringPiece(const TChar* str); // NOLINT(implicit)
+ BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(google-explicit-constructor)
+ BasicStringPiece(const TChar* str); // NOLINT(google-explicit-constructor)
BasicStringPiece(const TChar* str, size_t len);
BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h
index 5cfe54e5..fb2fad6 100644
--- a/libs/androidfw/include/androidfw/TypeWrappers.h
+++ b/libs/androidfw/include/androidfw/TypeWrappers.h
@@ -23,7 +23,7 @@
namespace android {
struct TypeVariant {
- TypeVariant(const ResTable_type* data);
+ explicit TypeVariant(const ResTable_type* data);
class iterator {
public:
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index 10d088e..aa1466f 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -46,7 +46,7 @@
using pointer = typename std::add_pointer<T>::type;
constexpr unique_cptr() : ptr_(nullptr) {}
- constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
+ constexpr explicit unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; }
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 562a3b2..1661905 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -23,6 +23,7 @@
#include "FileBlobCache.h"
#include "Properties.h"
#include "utils/TraceUtils.h"
+#include <GrContext.h>
namespace android {
namespace uirenderer {
@@ -168,6 +169,24 @@
const void* value = data.data();
BlobCache* bc = getBlobCacheLocked();
+ if (mInStoreVkPipelineInProgress) {
+ if (mOldPipelineCacheSize == -1) {
+ // Record the initial pipeline cache size stored in the file.
+ mOldPipelineCacheSize = bc->get(key.data(), keySize, nullptr, 0);
+ }
+ if (mNewPipelineCacheSize != -1 && mNewPipelineCacheSize == valueSize) {
+ // There has not been change in pipeline cache size. Stop trying to save.
+ mTryToStorePipelineCache = false;
+ return;
+ }
+ mNewPipelineCacheSize = valueSize;
+ } else {
+ mCacheDirty = true;
+ // If there are new shaders compiled, we probably have new pipeline state too.
+ // Store pipeline cache on the next flush.
+ mNewPipelineCacheSize = -1;
+ mTryToStorePipelineCache = true;
+ }
bc->set(key.data(), keySize, value, valueSize);
if (!mSavePending && mDeferredSaveDelay > 0) {
@@ -175,12 +194,31 @@
std::thread deferredSaveThread([this]() {
sleep(mDeferredSaveDelay);
std::lock_guard<std::mutex> lock(mMutex);
- saveToDiskLocked();
+ // Store file on disk if there a new shader or Vulkan pipeline cache size changed.
+ if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
+ saveToDiskLocked();
+ mOldPipelineCacheSize = mNewPipelineCacheSize;
+ mTryToStorePipelineCache = false;
+ mCacheDirty = false;
+ }
});
deferredSaveThread.detach();
}
}
+void ShaderCache::onVkFrameFlushed(GrContext* context) {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!mInitialized || !mTryToStorePipelineCache) {
+ return;
+ }
+ }
+ mInStoreVkPipelineInProgress = true;
+ context->storeVkPipelineCacheData();
+ mInStoreVkPipelineInProgress = false;
+}
+
} /* namespace skiapipeline */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index d41aadb..0898017 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -75,6 +75,13 @@
*/
void store(const SkData& key, const SkData& data) override;
+ /**
+ * "onVkFrameFlushed" tries to store Vulkan pipeline cache state.
+ * Pipeline cache is saved on disk only if the size of the data has changed or there was
+ * a new shader compiled.
+ */
+ void onVkFrameFlushed(GrContext* context);
+
private:
// Creation and (the lack of) destruction is handled internally.
ShaderCache();
@@ -167,6 +174,33 @@
mutable std::mutex mMutex;
/**
+ * If set to "true", the next call to onVkFrameFlushed, will invoke
+ * GrCanvas::storeVkPipelineCacheData. This does not guarantee that data will be stored on disk.
+ */
+ bool mTryToStorePipelineCache = true;
+
+ /**
+ * This flag is used by "ShaderCache::store" to distinguish between shader data and
+ * Vulkan pipeline data.
+ */
+ bool mInStoreVkPipelineInProgress = false;
+
+ /**
+ * "mNewPipelineCacheSize" has the size of the new Vulkan pipeline cache data. It is used
+ * to prevent unnecessary disk writes, if the pipeline cache size has not changed.
+ */
+ size_t mNewPipelineCacheSize = -1;
+ /**
+ * "mOldPipelineCacheSize" has the size of the Vulkan pipeline cache data stored on disk.
+ */
+ size_t mOldPipelineCacheSize = -1;
+
+ /**
+ * "mCacheDirty" is true when there is new shader cache data, which is not saved to disk.
+ */
+ bool mCacheDirty = false;
+
+ /**
* "sCache" is the singleton ShaderCache object.
*/
static ShaderCache sCache;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 1d3a244..b9aae98 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -23,6 +23,7 @@
#include "VkInteropFunctorDrawable.h"
#include "renderstate/RenderState.h"
#include "renderthread/Frame.h"
+#include "ShaderCache.h"
#include <SkSurface.h>
#include <SkTypes.h>
@@ -73,6 +74,7 @@
}
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer);
+ ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext());
layerUpdateQueue->clear();
// Draw visual debugging features
diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h
index b2351fc..fd824bd 100644
--- a/libs/hwui/private/hwui/DrawVkInfo.h
+++ b/libs/hwui/private/hwui/DrawVkInfo.h
@@ -52,17 +52,8 @@
// Input: Format of the destination surface.
VkFormat format;
- // Input: Color space transfer params
- float g;
- float a;
- float b;
- float c;
- float d;
- float e;
- float f;
-
- // Input: Color space transformation from linear RGB to D50-adapted XYZ
- float colorSpaceTransform[9];
+ // Input: Color space
+ const SkColorSpace* colorSpaceInfo;
// Input: current clip rect
int clipLeft;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 0375de3..bc9500d 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2289,6 +2289,30 @@
*/
public static final int ERROR_UNSUPPORTED_OPERATION = 6;
+ /**
+ * This indicates that the security level of the device is not
+ * sufficient to meet the requirements set by the content owner
+ * in the license policy.
+ */
+ public static final int ERROR_INSUFFICIENT_SECURITY = 7;
+
+ /**
+ * This indicates that the video frame being decrypted exceeds
+ * the size of the device's protected output buffers. When
+ * encountering this error the app should try playing content
+ * of a lower resolution.
+ */
+ public static final int ERROR_FRAME_TOO_LARGE = 8;
+
+ /**
+ * This error indicates that session state has been
+ * invalidated. It can occur on devices that are not capable
+ * of retaining crypto session state across device
+ * suspend/resume. The session must be closed and a new
+ * session opened to resume operation.
+ */
+ public static final int ERROR_LOST_STATE = 9;
+
/** @hide */
@IntDef({
ERROR_NO_KEY,
@@ -2296,7 +2320,10 @@
ERROR_RESOURCE_BUSY,
ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
ERROR_SESSION_NOT_OPENED,
- ERROR_UNSUPPORTED_OPERATION
+ ERROR_UNSUPPORTED_OPERATION,
+ ERROR_INSUFFICIENT_SECURITY,
+ ERROR_FRAME_TOO_LARGE,
+ ERROR_LOST_STATE
})
@Retention(RetentionPolicy.SOURCE)
public @interface CryptoErrorCode {}
diff --git a/media/java/android/media/MediaConstants.java b/media/java/android/media/MediaConstants.java
index 275b0ac..5a5747a 100644
--- a/media/java/android/media/MediaConstants.java
+++ b/media/java/android/media/MediaConstants.java
@@ -24,7 +24,7 @@
static final String KEY_PACKAGE_NAME = "android.media.key.PACKAGE_NAME";
// Bundle key for Parcelable
- static final String KEY_SESSION2_STUB = "android.media.key.SESSION2_STUB";
+ static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK";
static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS";
private MediaConstants() {
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index b8381a7..774ea18 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -19,7 +19,7 @@
import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
import static android.media.MediaConstants.KEY_PACKAGE_NAME;
import static android.media.MediaConstants.KEY_PID;
-import static android.media.MediaConstants.KEY_SESSION2_STUB;
+import static android.media.MediaConstants.KEY_SESSION2LINK;
import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
import static android.media.Session2Command.RESULT_INFO_SKIPPED;
import static android.media.Session2Token.TYPE_SESSION;
@@ -41,15 +41,15 @@
/**
* Allows an app to interact with an active {@link MediaSession2} or a
- * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other
+ * MediaSession2Service which would provide {@link MediaSession2}. Media buttons and other
* commands can be sent to the session.
* <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
* <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
* for consistent behavior across all devices.
- * @hide
*/
+// TODO: use @link for MediaSession2Service
public class MediaController2 implements AutoCloseable {
static final String TAG = "MediaController2";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -130,8 +130,8 @@
synchronized (mLock) {
if (mSessionBinder != null) {
try {
- mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
mSessionBinder.disconnect(mControllerStub, getNextSeqNumber());
+ mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
} catch (RuntimeException e) {
// No-op
}
@@ -153,6 +153,7 @@
* @return a token which will be sent together in {@link ControllerCallback#onCommandResult}
* when its result is received.
*/
+ @NonNull
public Object sendSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
if (command == null) {
throw new IllegalArgumentException("command shouldn't be null");
@@ -208,7 +209,7 @@
void onConnected(int seq, Bundle connectionResult) {
final long token = Binder.clearCallingIdentity();
try {
- Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2_STUB);
+ Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK);
Session2CommandGroup allowedCommands =
connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
if (DEBUG) {
@@ -349,7 +350,7 @@
* @return the result for the session command. A runtime exception will be thrown if null
* is returned.
*/
- @NonNull
+ @Nullable
public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller,
@NonNull Session2Command command, @Nullable Bundle args) {
return null;
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index cdbc7b44..75b3915 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -132,11 +132,19 @@
private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES;
private EventHandler mEventHandler;
- private EventHandler mOnKeyStatusChangeEventHandler;
- private EventHandler mOnExpirationUpdateEventHandler;
+ private EventHandler mKeyStatusChangeHandler;
+ private EventHandler mExpirationUpdateHandler;
+ private EventHandler mSessionLostStateHandler;
+
private OnEventListener mOnEventListener;
private OnKeyStatusChangeListener mOnKeyStatusChangeListener;
private OnExpirationUpdateListener mOnExpirationUpdateListener;
+ private OnSessionLostStateListener mOnSessionLostStateListener;
+
+ private final Object mEventLock = new Object();
+ private final Object mKeyStatusChangeLock = new Object();
+ private final Object mExpirationUpdateLock = new Object();
+ private final Object mSessionLostStateLock = new Object();
private long mNativeContext;
@@ -200,6 +208,35 @@
private static final native boolean isCryptoSchemeSupportedNative(
@NonNull byte[] uuid, @Nullable String mimeType);
+ private EventHandler createHandler() {
+ Looper looper;
+ EventHandler handler;
+ if ((looper = Looper.myLooper()) != null) {
+ handler = new EventHandler(this, looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ handler = new EventHandler(this, looper);
+ } else {
+ handler = null;
+ }
+ return handler;
+ }
+
+ private EventHandler updateHandler(Handler handler) {
+ Looper looper;
+ EventHandler newHandler = null;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ looper = Looper.myLooper();
+ }
+ if (looper != null) {
+ if (handler == null || handler.getLooper() != looper) {
+ newHandler = new EventHandler(this, looper);
+ }
+ }
+ return newHandler;
+ }
+
/**
* Instantiate a MediaDrm object
*
@@ -209,14 +246,10 @@
* specified scheme UUID
*/
public MediaDrm(@NonNull UUID uuid) throws UnsupportedSchemeException {
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else {
- mEventHandler = null;
- }
+ mEventHandler = createHandler();
+ mKeyStatusChangeHandler = createHandler();
+ mExpirationUpdateHandler = createHandler();
+ mSessionLostStateHandler = createHandler();
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
@@ -272,6 +305,40 @@
}
/**
+ * Thrown when an error occurs in any method that has a session context.
+ */
+ public static final class SessionException extends RuntimeException {
+ public SessionException(int errorCode, @Nullable String detailMessage) {
+ super(detailMessage);
+ mErrorCode = errorCode;
+ }
+
+ /**
+ * This indicates that apps using MediaDrm sessions are
+ * temporarily exceeding the capacity of available crypto
+ * resources. The app should retry the operation later.
+ */
+ public static final int ERROR_RESOURCE_CONTENTION = 1;
+
+ /** @hide */
+ @IntDef({
+ ERROR_RESOURCE_CONTENTION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SessionErrorCode {}
+
+ /**
+ * Retrieve the error code associated with the SessionException
+ */
+ @SessionErrorCode
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ private final int mErrorCode;
+ }
+
+ /**
* Register a callback to be invoked when a session expiration update
* occurs. The app's OnExpirationUpdateListener will be notified
* when the expiration time of the keys in the session have changed.
@@ -282,15 +349,12 @@
*/
public void setOnExpirationUpdateListener(
@Nullable OnExpirationUpdateListener listener, @Nullable Handler handler) {
- if (listener != null) {
- Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
- if (looper != null) {
- if (mEventHandler == null || mEventHandler.getLooper() != looper) {
- mEventHandler = new EventHandler(this, looper);
- }
+ synchronized(mExpirationUpdateLock) {
+ if (listener != null) {
+ mExpirationUpdateHandler = updateHandler(handler);
}
+ mOnExpirationUpdateListener = listener;
}
- mOnExpirationUpdateListener = listener;
}
/**
@@ -324,15 +388,12 @@
*/
public void setOnKeyStatusChangeListener(
@Nullable OnKeyStatusChangeListener listener, @Nullable Handler handler) {
- if (listener != null) {
- Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
- if (looper != null) {
- if (mEventHandler == null || mEventHandler.getLooper() != looper) {
- mEventHandler = new EventHandler(this, looper);
- }
+ synchronized(mKeyStatusChangeLock) {
+ if (listener != null) {
+ mKeyStatusChangeHandler = updateHandler(handler);
}
+ mOnKeyStatusChangeListener = listener;
}
- mOnKeyStatusChangeListener = listener;
}
/**
@@ -360,6 +421,46 @@
}
/**
+ * Register a callback to be invoked when session state has been
+ * lost. This event can occur on devices that are not capable of
+ * retaining crypto session state across device suspend/resume
+ * cycles. When this event occurs, the session must be closed and
+ * a new session opened to resume operation.
+ *
+ * @param listener the callback that will be run, or {@code null} to unregister the
+ * previously registered callback.
+ * @param handler the handler on which the listener should be invoked, or
+ * {@code null} if the listener should be invoked on the calling thread's looper.
+ */
+ public void setOnSessionLostStateListener(
+ @Nullable OnSessionLostStateListener listener, @Nullable Handler handler) {
+ synchronized(mSessionLostStateLock) {
+ if (listener != null) {
+ mSessionLostStateHandler = updateHandler(handler);
+ }
+ mOnSessionLostStateListener = listener;
+ }
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the
+ * session state has been lost and is now invalid
+ */
+ public interface OnSessionLostStateListener
+ {
+ /**
+ * Called when session state has lost state, to inform the app
+ * about the condition so it can close the session and open a new
+ * one to resume operation.
+ *
+ * @param md the MediaDrm object on which the event occurred
+ * @param sessionId the DRM session ID on which the event occurred
+ */
+ void onSessionLostState(
+ @NonNull MediaDrm md, @NonNull byte[] sessionId);
+ }
+
+ /**
* Defines the status of a key.
* A KeyStatus for each key in a session is provided to the
* {@link OnKeyStatusChangeListener#onKeyStatusChange}
@@ -437,7 +538,9 @@
*/
public void setOnEventListener(@Nullable OnEventListener listener)
{
- mOnEventListener = listener;
+ synchronized(mEventLock) {
+ mOnEventListener = listener;
+ }
}
/**
@@ -513,6 +616,7 @@
private static final int DRM_EVENT = 200;
private static final int EXPIRATION_UPDATE = 201;
private static final int KEY_STATUS_CHANGE = 202;
+ private static final int SESSION_LOST_STATE = 203;
private class EventHandler extends Handler
{
@@ -532,52 +636,72 @@
switch(msg.what) {
case DRM_EVENT:
- if (mOnEventListener != null) {
- if (msg.obj != null && msg.obj instanceof Parcel) {
- Parcel parcel = (Parcel)msg.obj;
- byte[] sessionId = parcel.createByteArray();
- if (sessionId.length == 0) {
- sessionId = null;
- }
- byte[] data = parcel.createByteArray();
- if (data.length == 0) {
- data = null;
- }
+ synchronized(mEventLock) {
+ if (mOnEventListener != null) {
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ if (sessionId.length == 0) {
+ sessionId = null;
+ }
+ byte[] data = parcel.createByteArray();
+ if (data.length == 0) {
+ data = null;
+ }
- Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
- mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+ Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
+ mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+ }
}
}
return;
case KEY_STATUS_CHANGE:
- if (mOnKeyStatusChangeListener != null) {
- if (msg.obj != null && msg.obj instanceof Parcel) {
- Parcel parcel = (Parcel)msg.obj;
- byte[] sessionId = parcel.createByteArray();
- if (sessionId.length > 0) {
- List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel);
- boolean hasNewUsableKey = (parcel.readInt() != 0);
+ synchronized(mKeyStatusChangeLock) {
+ if (mOnKeyStatusChangeListener != null) {
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ if (sessionId.length > 0) {
+ List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel);
+ boolean hasNewUsableKey = (parcel.readInt() != 0);
- Log.i(TAG, "Drm key status changed");
- mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId,
- keyStatusList, hasNewUsableKey);
+ Log.i(TAG, "Drm key status changed");
+ mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId,
+ keyStatusList, hasNewUsableKey);
+ }
}
}
}
return;
case EXPIRATION_UPDATE:
- if (mOnExpirationUpdateListener != null) {
- if (msg.obj != null && msg.obj instanceof Parcel) {
- Parcel parcel = (Parcel)msg.obj;
- byte[] sessionId = parcel.createByteArray();
- if (sessionId.length > 0) {
- long expirationTime = parcel.readLong();
+ synchronized(mExpirationUpdateLock) {
+ if (mOnExpirationUpdateListener != null) {
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ if (sessionId.length > 0) {
+ long expirationTime = parcel.readLong();
- Log.i(TAG, "Drm key expiration update: " + expirationTime);
- mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId,
- expirationTime);
+ Log.i(TAG, "Drm key expiration update: " + expirationTime);
+ mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId,
+ expirationTime);
+ }
+ }
+ }
+ }
+ return;
+
+ case SESSION_LOST_STATE:
+ synchronized(mSessionLostStateLock) {
+ if (mOnSessionLostStateListener != null) {
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ Log.i(TAG, "Drm session lost state event: ");
+ mOnSessionLostStateListener.onSessionLostState(mMediaDrm,
+ sessionId);
}
}
}
@@ -619,9 +743,42 @@
if (md == null) {
return;
}
- if (md.mEventHandler != null) {
- Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj);
- md.mEventHandler.sendMessage(m);
+ switch (what) {
+ case DRM_EVENT:
+ synchronized(md.mEventLock) {
+ if (md.mEventHandler != null) {
+ Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj);
+ md.mEventHandler.sendMessage(m);
+ }
+ }
+ break;
+ case EXPIRATION_UPDATE:
+ synchronized(md.mExpirationUpdateLock) {
+ if (md.mExpirationUpdateHandler != null) {
+ Message m = md.mExpirationUpdateHandler.obtainMessage(what, obj);
+ md.mExpirationUpdateHandler.sendMessage(m);
+ }
+ }
+ break;
+ case KEY_STATUS_CHANGE:
+ synchronized(md.mKeyStatusChangeLock) {
+ if (md.mKeyStatusChangeHandler != null) {
+ Message m = md.mKeyStatusChangeHandler.obtainMessage(what, obj);
+ md.mKeyStatusChangeHandler.sendMessage(m);
+ }
+ }
+ break;
+ case SESSION_LOST_STATE:
+ synchronized(md.mSessionLostStateLock) {
+ if (md.mSessionLostStateHandler != null) {
+ Message m = md.mSessionLostStateHandler.obtainMessage(what, obj);
+ md.mSessionLostStateHandler.sendMessage(m);
+ }
+ }
+ break;
+ default:
+ Log.e(TAG, "Unknown message type " + what);
+ break;
}
}
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 7b20f7a..e008adf 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -19,7 +19,7 @@
import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
import static android.media.MediaConstants.KEY_PACKAGE_NAME;
import static android.media.MediaConstants.KEY_PID;
-import static android.media.MediaConstants.KEY_SESSION2_STUB;
+import static android.media.MediaConstants.KEY_SESSION2LINK;
import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
import static android.media.Session2Command.RESULT_INFO_SKIPPED;
import static android.media.Session2Token.TYPE_SESSION;
@@ -56,10 +56,9 @@
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
* <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
* for consistent behavior across all devices.
- * @hide
*/
public class MediaSession2 implements AutoCloseable {
- static final String TAG = "MediaSession";
+ static final String TAG = "MediaSession2";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// Note: This checks the uniqueness of a session ID only in a single process.
@@ -89,7 +88,6 @@
private final Handler mResultHandler;
//@GuardedBy("mLock")
- @SuppressWarnings("WeakerAccess") /* synthetic access */
private boolean mClosed;
MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity,
@@ -113,6 +111,7 @@
Context.MEDIA_SESSION_SERVICE);
// NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
mResultHandler = new Handler(context.getMainLooper());
+ mClosed = false;
}
@Override
@@ -179,6 +178,7 @@
* @return a token which will be sent together in {@link SessionCallback#onCommandResult}
* when its result is received.
*/
+ @NonNull
public Object sendSessionCommand(@NonNull ControllerInfo controller,
@NonNull Session2Command command, @Nullable Bundle args) {
if (controller == null) {
@@ -206,7 +206,10 @@
* @param controller the controller to get the session command
* @param token the token which is returned from {@link #sendSessionCommand}.
*/
- public void cancelSessionCommand(ControllerInfo controller, Object token) {
+ public void cancelSessionCommand(@NonNull ControllerInfo controller, @NonNull Object token) {
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
if (token == null) {
throw new IllegalArgumentException("token shouldn't be null");
}
@@ -267,7 +270,7 @@
// It's needed because we cannot call synchronous calls between
// session/controller.
Bundle connectionResult = new Bundle();
- connectionResult.putParcelable(KEY_SESSION2_STUB, mSessionStub);
+ connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub);
connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
controllerInfo.mAllowedCommands);
@@ -558,7 +561,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof ControllerInfo)) return false;
if (this == obj) return true;
@@ -570,6 +573,7 @@
}
@Override
+ @NonNull
public String toString() {
return "ControllerInfo {pkg=" + mRemoteUserInfo.getPackageName() + ", uid="
+ mRemoteUserInfo.getUid() + ", allowedCommands=" + mAllowedCommands + "})";
@@ -693,7 +697,7 @@
* @return the result for the session command. A runtime exception will be thrown if null
* is returned.
*/
- @NonNull
+ @Nullable
public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull Session2Command command,
@Nullable Bundle args) {
diff --git a/media/java/android/media/Session2CommandGroup.java b/media/java/android/media/Session2CommandGroup.java
index 519888e..a189c26 100644
--- a/media/java/android/media/Session2CommandGroup.java
+++ b/media/java/android/media/Session2CommandGroup.java
@@ -59,6 +59,7 @@
*
* @param commands The collection of commands to copy.
*/
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
Session2CommandGroup(@Nullable Collection<Session2Command> commands) {
if (commands != null) {
mCommands.addAll(commands);
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java
index e1fff38..95ee4c0 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/java/android/media/Session2Token.java
@@ -34,7 +34,7 @@
import java.util.Objects;
/**
- * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
+ * Represents an ongoing {@link MediaSession2} or a MediaSession2Service.
* If it's representing a session service, it may not be ongoing.
* <p>
* This API is not generally intended for third party application developers.
@@ -45,9 +45,8 @@
* This may be passed to apps by the session owner to allow them to create a
* {@link MediaController2} to communicate with the session.
* <p>
- * It can be also obtained by {@link MediaSessionManager}.
+ * It can be also obtained by {@link android.media.session.MediaSessionManager}.
*
- * @hide
*/
// New version of MediaSession2.Token for following reasons
// - Stop implementing Parcelable for updatable support
@@ -56,6 +55,7 @@
// This helps controller apps to keep target of dispatching media key events in uniform way.
// For details about the reason, see following. (Android O+)
// android.media.session.MediaSessionManager.Callback#onAddressedPlayerChanged
+// TODO: use @link for MediaSession2Service
public final class Session2Token implements Parcelable {
private static final String TAG = "Session2Token";
@@ -75,7 +75,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
+ @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE})
public @interface TokenType {
}
@@ -85,15 +85,10 @@
public static final int TYPE_SESSION = 0;
/**
- * Type for {@link MediaSession2Service}.
+ * Type for MediaSession2Service.
*/
public static final int TYPE_SESSION_SERVICE = 1;
- /**
- * Type for {@link MediaLibrary2Service}.
- */
- public static final int TYPE_LIBRARY_SERVICE = 2;
-
private final int mUid;
private final @TokenType int mType;
private final String mPackageName;
@@ -102,8 +97,7 @@
private final ComponentName mComponentName;
/**
- * Constructor for the token with type {@link #TYPE_SESSION_SERVICE} or
- * {@link #TYPE_LIBRARY_SERVICE}.
+ * Constructor for the token with type {@link #TYPE_SESSION_SERVICE}.
*
* @param context The context.
* @param serviceComponent The component name of the service.
@@ -239,7 +233,6 @@
* @return type of the token
* @see #TYPE_SESSION
* @see #TYPE_SESSION_SERVICE
- * @see #TYPE_LIBRARY_SERVICE
*/
public @TokenType int getType() {
return mType;
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 2578608..7b07bea3 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -72,7 +72,10 @@
jint cryptoErrorResourceBusy;
jint cryptoErrorInsufficientOutputProtection;
jint cryptoErrorSessionNotOpened;
+ jint cryptoErrorInsufficientSecurity;
jint cryptoErrorUnsupportedOperation;
+ jint cryptoErrorFrameTooLarge;
+ jint cryptoErrorLostState;
} gCryptoErrorCodes;
static struct CodecActionCodes {
@@ -1005,10 +1008,22 @@
err = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
defaultMsg = "Attempted to use a closed session";
break;
+ case ERROR_DRM_INSUFFICIENT_SECURITY:
+ err = gCryptoErrorCodes.cryptoErrorInsufficientSecurity;
+ defaultMsg = "Required security level is not met";
+ break;
case ERROR_DRM_CANNOT_HANDLE:
err = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
defaultMsg = "Operation not supported in this configuration";
break;
+ case ERROR_DRM_FRAME_TOO_LARGE:
+ err = gCryptoErrorCodes.cryptoErrorFrameTooLarge;
+ defaultMsg = "Decrytped frame exceeds size of output buffer";
+ break;
+ case ERROR_DRM_SESSION_LOST_STATE:
+ err = gCryptoErrorCodes.cryptoErrorLostState;
+ defaultMsg = "Session state was lost, open a new session and retry";
+ break;
default: /* Other negative DRM error codes go out as is. */
break;
}
@@ -1994,11 +2009,26 @@
gCryptoErrorCodes.cryptoErrorSessionNotOpened =
env->GetStaticIntField(clazz.get(), field);
+ field = env->GetStaticFieldID(clazz.get(), "ERROR_INSUFFICIENT_SECURITY", "I");
+ CHECK(field != NULL);
+ gCryptoErrorCodes.cryptoErrorInsufficientSecurity =
+ env->GetStaticIntField(clazz.get(), field);
+
field = env->GetStaticFieldID(clazz.get(), "ERROR_UNSUPPORTED_OPERATION", "I");
CHECK(field != NULL);
gCryptoErrorCodes.cryptoErrorUnsupportedOperation =
env->GetStaticIntField(clazz.get(), field);
+ field = env->GetStaticFieldID(clazz.get(), "ERROR_FRAME_TOO_LARGE", "I");
+ CHECK(field != NULL);
+ gCryptoErrorCodes.cryptoErrorFrameTooLarge =
+ env->GetStaticIntField(clazz.get(), field);
+
+ field = env->GetStaticFieldID(clazz.get(), "ERROR_LOST_STATE", "I");
+ CHECK(field != NULL);
+ gCryptoErrorCodes.cryptoErrorLostState =
+ env->GetStaticIntField(clazz.get(), field);
+
clazz.reset(env->FindClass("android/media/MediaCodec$CodecException"));
CHECK(clazz.get() != NULL);
field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I");
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index be71dad5..8336459 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -110,6 +110,7 @@
jint kWhatDrmEvent;
jint kWhatExpirationUpdate;
jint kWhatKeyStatusChange;
+ jint kWhatSessionLostState;
} gEventWhat;
struct KeyTypes {
@@ -141,6 +142,16 @@
jclass classId;
};
+struct SessionExceptionFields {
+ jmethodID init;
+ jclass classId;
+ jfieldID errorCode;
+};
+
+struct SessionExceptionErrorCodes {
+ jint kResourceContention;
+} gSessionExceptionErrorCodes;
+
struct HDCPLevels {
jint kHdcpLevelUnknown;
jint kHdcpNone;
@@ -180,6 +191,7 @@
EntryFields entry;
CertificateFields certificate;
StateExceptionFields stateException;
+ SessionExceptionFields sessionException;
jclass certificateClassId;
jclass hashmapClassId;
jclass arraylistClassId;
@@ -310,6 +322,9 @@
case DrmPlugin::kDrmPluginEventKeysChange:
jwhat = gEventWhat.kWhatKeyStatusChange;
break;
+ case DrmPlugin::kDrmPluginEventSessionLostState:
+ jwhat = gEventWhat.kWhatSessionLostState;
+ break;
default:
ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
return;
@@ -343,6 +358,30 @@
env->Throw(static_cast<jthrowable>(exception));
}
+static void throwSessionException(JNIEnv *env, const char *msg, status_t err) {
+ ALOGE("Session exception: %s (%d)", msg, err);
+
+ jint jErrorCode = 0;
+ switch(err) {
+ case ERROR_DRM_RESOURCE_CONTENTION:
+ jErrorCode = gSessionExceptionErrorCodes.kResourceContention;
+ break;
+ default:
+ break;
+ }
+
+ jobject exception = env->NewObject(gFields.sessionException.classId,
+ gFields.sessionException.init, static_cast<int>(err),
+ env->NewStringUTF(msg));
+
+ env->SetIntField(exception, gFields.sessionException.errorCode, jErrorCode);
+ env->Throw(static_cast<jthrowable>(exception));
+}
+
+static bool isSessionException(status_t err) {
+ return err == ERROR_DRM_RESOURCE_CONTENTION;
+}
+
static bool throwExceptionAsNecessary(
JNIEnv *env, status_t err, const char *msg = NULL) {
@@ -370,7 +409,7 @@
case ERROR_DRM_CANNOT_HANDLE:
drmMessage = "Invalid parameter or data format";
break;
- case ERROR_DRM_TAMPER_DETECTED:
+ case ERROR_DRM_INVALID_STATE:
drmMessage = "Invalid state";
break;
default:
@@ -399,6 +438,9 @@
jniThrowException(env, "android/media/MediaDrmResetException",
"mediaserver died");
return true;
+ } else if (isSessionException(err)) {
+ throwSessionException(env, msg, err);
+ return true;
} else if (err != OK) {
String8 errbuf;
if (drmMessage != NULL) {
@@ -705,6 +747,8 @@
gEventWhat.kWhatExpirationUpdate = env->GetStaticIntField(clazz, field);
GET_STATIC_FIELD_ID(field, clazz, "KEY_STATUS_CHANGE", "I");
gEventWhat.kWhatKeyStatusChange = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "SESSION_LOST_STATE", "I");
+ gEventWhat.kWhatSessionLostState = env->GetStaticIntField(clazz, field);
GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I");
gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field);
@@ -831,6 +875,14 @@
FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
+
+ FIND_CLASS(clazz, "android/media/MediaDrm$SessionException");
+ GET_METHOD_ID(gFields.sessionException.init, clazz, "<init>", "(ILjava/lang/String;)V");
+ gFields.sessionException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
+ GET_FIELD_ID(gFields.sessionException.errorCode, clazz, "mErrorCode", "I");
+
+ GET_STATIC_FIELD_ID(field, clazz, "ERROR_RESOURCE_CONTENTION", "I");
+ gSessionExceptionErrorCodes.kResourceContention = env->GetStaticIntField(clazz, field);
}
static void android_media_MediaDrm_native_setup(
diff --git a/native/android/sharedmem.cpp b/native/android/sharedmem.cpp
index 757aaec..4410bd6 100644
--- a/native/android/sharedmem.cpp
+++ b/native/android/sharedmem.cpp
@@ -71,7 +71,7 @@
}
int fd = env->CallIntMethod(javaSharedMemory, sSharedMemory.getFd);
if (fd != -1) {
- fd = dup(fd);
+ fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
}
return fd;
}
diff --git a/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h
index 6afd883..bb2ee9b 100644
--- a/native/webview/plat_support/draw_fn.h
+++ b/native/webview/plat_support/draw_fn.h
@@ -109,8 +109,15 @@
// Input: Format of the destination surface.
VkFormat format;
- // Input: Color space transformation from linear RGB to D50-adapted XYZ
- float matrix[9];
+ // Input: Color space parameters.
+ float transfer_function_g;
+ float transfer_function_a;
+ float transfer_function_b;
+ float transfer_function_c;
+ float transfer_function_d;
+ float transfer_function_e;
+ float transfer_function_f;
+ float color_space_toXYZD50[9];
// Input: current clip rect
int clip_left;
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 838b88b..499d493 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -286,6 +286,9 @@
if (!isForCurrentUser(sbn)) {
return;
}
+
+ mAgingHelper.onNotificationRemoved(sbn.getKey());
+
boolean updatedImpressions = false;
String channelId = mLiveNotifications.remove(sbn.getKey()).getChannel().getId();
String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId);
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index a9d8f62..95df5f2 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -27,6 +27,7 @@
import android.service.notification.NotificationAssistantService;
import android.text.TextUtils;
import android.util.LruCache;
+import android.view.textclassifier.ConversationAction;
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
@@ -65,13 +66,13 @@
private static final int MAX_MESSAGES_TO_EXTRACT = 5;
private static final int MAX_RESULT_ID_TO_CACHE = 20;
- private static final ConversationActions.TypeConfig TYPE_CONFIG =
- new ConversationActions.TypeConfig.Builder().setIncludedTypes(
- Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+ private static final TextClassifier.EntityConfig TYPE_CONFIG =
+ new TextClassifier.EntityConfig.Builder().setIncludedTypes(
+ Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
.includeTypesFromTextClassifier(false)
.build();
private static final List<String> HINTS =
- Collections.singletonList(ConversationActions.HINT_FOR_NOTIFICATION);
+ Collections.singletonList(ConversationActions.Request.HINT_FOR_NOTIFICATION);
private Context mContext;
@Nullable
@@ -137,7 +138,7 @@
ConversationActions conversationActionsResult =
mTextClassifier.suggestConversationActions(request);
- List<ConversationActions.ConversationAction> conversationActions =
+ List<ConversationAction> conversationActions =
conversationActionsResult.getConversationActions();
ArrayList<CharSequence> replies = conversationActions.stream()
.map(conversationAction -> conversationAction.getTextReply())
@@ -193,7 +194,7 @@
}
TextClassifierEvent textClassifierEvent =
createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
- .setEntityType(ConversationActions.TYPE_TEXT_REPLY)
+ .setEntityType(ConversationAction.TYPE_TEXT_REPLY)
.build();
mTextClassifier.onTextClassifierEvent(textClassifierEvent);
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
index 7d74788..7b7ce3d 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
@@ -32,6 +32,7 @@
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
+import android.view.textclassifier.ConversationAction;
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
@@ -65,9 +66,10 @@
private static final String NOTIFICATION_KEY = "key";
private static final String RESULT_ID = "id";
- private static final ConversationActions.ConversationAction REPLY_ACTION =
- new ConversationActions.ConversationAction.Builder(
- ConversationActions.TYPE_TEXT_REPLY).setTextReply("Home").build();
+ private static final ConversationAction REPLY_ACTION =
+ new ConversationAction.Builder(ConversationAction.TYPE_TEXT_REPLY)
+ .setTextReply("Home")
+ .build();
private SmartActionsHelper mSmartActionsHelper;
private Context mContext;
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 0b0f1ec..7f8bb93 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -20,6 +20,7 @@
package="com.android.mainline.networkstack"
android:sharedUserId="android.uid.networkstack">
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 94ea1b9..4077d93 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -545,7 +545,9 @@
return HANDLED;
case CMD_FORCE_REEVALUATION:
case CMD_CAPTIVE_PORTAL_RECHECK:
- log("Forcing reevaluation for UID " + message.arg1);
+ final int dnsCount = mDnsStallDetector.getConsecutiveTimeoutCount();
+ validationLog("Forcing reevaluation for UID " + message.arg1
+ + ". Dns signal count: " + dnsCount);
mUidResponsibleForReeval = message.arg1;
transitionTo(mEvaluatingState);
return HANDLED;
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
index aed02a2..8090169 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
@@ -41,10 +41,6 @@
private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN;
private long mVisibleTimestamp;
- private VisibilityLoggerMixin() {
- mMetricsCategory = METRICS_CATEGORY_UNKNOWN;
- }
-
public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) {
mMetricsCategory = metricsCategory;
mMetricsFeature = metricsFeature;
@@ -81,12 +77,4 @@
MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
MetricsProto.MetricsEvent.VIEW_UNKNOWN);
}
-
- /** Returns elapsed time since onResume() */
- public long elapsedTimeSinceVisible() {
- if (mVisibleTimestamp == 0) {
- return 0;
- }
- return SystemClock.elapsedRealtime() - mVisibleTimestamp;
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 4f81daf..af5a24f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -332,9 +332,9 @@
ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
bssid = config.BSSID;
security = getSecurity(config);
- updateKey();
networkId = config.networkId;
mConfig = config;
+ updateKey();
}
/** Updates {@link #mKey} and should only called upon object creation/initialization. */
@@ -343,7 +343,9 @@
StringBuilder builder = new StringBuilder();
- if (TextUtils.isEmpty(getSsidStr())) {
+ if (isPasspoint()) {
+ builder.append(mConfig.FQDN);
+ } else if (TextUtils.isEmpty(getSsidStr())) {
builder.append(getBssid());
} else {
builder.append(getSsidStr());
@@ -606,7 +608,9 @@
public static String getKey(WifiConfiguration config) {
StringBuilder builder = new StringBuilder();
- if (TextUtils.isEmpty(config.SSID)) {
+ if (config.isPasspoint()) {
+ builder.append(config.FQDN);
+ } else if (TextUtils.isEmpty(config.SSID)) {
builder.append(config.BSSID);
} else {
builder.append(removeDoubleQuotes(config.SSID));
@@ -621,9 +625,10 @@
}
public boolean matches(WifiConfiguration config) {
- if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
- return ssid.equals(removeDoubleQuotes(config.SSID)) && config.FQDN.equals(mConfig.FQDN);
+ if (config.isPasspoint()) {
+ return (isPasspoint() && config.FQDN.equals(mConfig.FQDN));
} else {
+ // Normal non-Passpoint network
return ssid.equals(removeDoubleQuotes(config.SSID))
&& security == getSecurity(config)
&& (mConfig == null || mConfig.shared == config.shared);
@@ -828,6 +833,17 @@
return "";
}
+ /**
+ * Returns the display title for the AccessPoint, such as for an AccessPointPreference's title.
+ */
+ public String getTitle() {
+ if (isPasspoint()) {
+ return mConfig.providerFriendlyName;
+ } else {
+ return getSsidStr();
+ }
+ }
+
public String getSummary() {
return getSettingsSummary(mConfig);
}
@@ -1048,18 +1064,21 @@
*/
void setScanResults(Collection<ScanResult> scanResults) {
- // Validate scan results are for current AP only
- String key = getKey();
- for (ScanResult result : scanResults) {
- String scanResultKey = AccessPoint.getKey(result);
- if (!mKey.equals(scanResultKey)) {
- throw new IllegalArgumentException(
- String.format("ScanResult %s\nkey of %s did not match current AP key %s",
- result, scanResultKey, key));
+ // Validate scan results are for current AP only by matching SSID/BSSID
+ // Passpoint R1 networks are not bound to a specific SSID/BSSID, so skip this for passpoint.
+ if (!isPasspoint()) {
+ String key = getKey();
+ for (ScanResult result : scanResults) {
+ String scanResultKey = AccessPoint.getKey(result);
+ if (!mKey.equals(scanResultKey)) {
+ throw new IllegalArgumentException(
+ String.format(
+ "ScanResult %s\nkey of %s did not match current AP key %s",
+ result, scanResultKey, key));
+ }
}
}
-
int oldLevel = getLevel();
mScanResults.clear();
mScanResults.addAll(scanResults);
@@ -1149,6 +1168,9 @@
void update(@Nullable WifiConfiguration config) {
mConfig = config;
+ if (mConfig != null) {
+ ssid = removeDoubleQuotes(mConfig.SSID);
+ }
networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID;
ThreadUtils.postOnMainThread(() -> {
if (mAccessPointListener != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index db364a3..1fa7083 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -266,7 +266,7 @@
if (savedNetworks) {
preference.setTitle(ap.getConfigName());
} else {
- preference.setTitle(ap.getSsidStr());
+ preference.setTitle(ap.getTitle());
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index e47ca32..79a7240 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -45,6 +45,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.widget.Toast;
import androidx.annotation.GuardedBy;
@@ -573,9 +574,52 @@
accessPoints.add(accessPoint);
}
+ List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values());
+
+ // Add a unique Passpoint R1 AccessPoint for each Passpoint profile's FQDN.
+ List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans =
+ mWifiManager.getAllMatchingWifiConfigs(cachedScanResults);
+ Set<String> seenFQDNs = new ArraySet<>();
+ for (Pair<WifiConfiguration,
+ Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) {
+ WifiConfiguration config = pairing.first;
+
+ // TODO: Prioritize home networks before roaming networks
+ List<ScanResult> scanResults = new ArrayList<>();
+
+ List<ScanResult> homeScans =
+ pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK);
+ List<ScanResult> roamingScans =
+ pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK);
+
+ if (homeScans == null) {
+ homeScans = new ArrayList<>();
+ }
+ if (roamingScans == null) {
+ roamingScans = new ArrayList<>();
+ }
+
+ scanResults.addAll(homeScans);
+ scanResults.addAll(roamingScans);
+
+ if (seenFQDNs.add(config.FQDN)) {
+ int bestRssi = Integer.MIN_VALUE;
+ for (ScanResult result : scanResults) {
+ if (result.level >= bestRssi) {
+ bestRssi = result.level;
+ config.SSID = AccessPoint.convertToQuotedString(result.SSID);
+ }
+ }
+
+ AccessPoint accessPoint = new AccessPoint(mContext, config);
+ accessPoint.setScanResults(scanResults);
+ accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
+ accessPoints.add(accessPoint);
+ }
+ }
+
// If there were no scan results, create an AP for the currently connected network (if
// it exists).
- // TODO(b/b/73076869): Add support for passpoint (ephemeral) networks
if (accessPoints.isEmpty() && connectionConfig != null) {
AccessPoint activeAp = new AccessPoint(mContext, connectionConfig);
activeAp.update(connectionConfig, mLastInfo, mLastNetworkInfo);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index e853399..ef90dc9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -28,8 +28,8 @@
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
-import android.media.AudioSystem;
import android.media.AudioManager;
+import android.media.AudioSystem;
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Environment;
@@ -1104,9 +1104,7 @@
}
if (upgradeVersion == 77) {
- // Introduce "vibrate when ringing" setting
- loadVibrateWhenRingingSetting(db);
-
+ // "vibrate when ringing" setting moved to SettingsProvider version 168
upgradeVersion = 78;
}
@@ -2223,8 +2221,6 @@
} finally {
if (stmt != null) stmt.close();
}
-
- loadVibrateWhenRingingSetting(db);
}
private void loadVibrateSetting(SQLiteDatabase db, boolean deleteOld) {
@@ -2250,24 +2246,6 @@
}
}
- private void loadVibrateWhenRingingSetting(SQLiteDatabase db) {
- // The default should be off. VIBRATE_SETTING_ONLY_SILENT should also be ignored here.
- // Phone app should separately check whether AudioManager#getRingerMode() returns
- // RINGER_MODE_VIBRATE, with which the device should vibrate anyway.
- int vibrateSetting = getIntValueFromSystem(db, Settings.System.VIBRATE_ON,
- AudioManager.VIBRATE_SETTING_OFF);
- boolean vibrateWhenRinging = ((vibrateSetting & 3) == AudioManager.VIBRATE_SETTING_ON);
-
- SQLiteStatement stmt = null;
- try {
- stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
- + " VALUES(?,?);");
- loadSetting(stmt, Settings.System.VIBRATE_WHEN_RINGING, vibrateWhenRinging ? 1 : 0);
- } finally {
- if (stmt != null) stmt.close();
- }
- }
-
private void loadSettings(SQLiteDatabase db) {
loadSystemSettings(db);
loadSecureSettings(db);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index fa95bf2..9b775e0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -129,6 +129,7 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+ <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
<uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 2530abc..afb9781 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -56,6 +56,7 @@
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.MainThread;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Notification;
@@ -799,6 +800,18 @@
Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
return;
}
+ final int max = intent.getIntExtra(EXTRA_MAX, -1);
+ final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
+ final String shareTitle = intent.getStringExtra(EXTRA_TITLE);
+ final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
+ onBugreportFinished(id, bugreportFile, screenshotFile, shareTitle, shareDescription, max);
+ }
+
+ /**
+ * Wraps up bugreport generation and triggers a notification to share the bugreport.
+ */
+ private void onBugreportFinished(int id, File bugreportFile, @Nullable File screenshotFile,
+ String shareTitle, String shareDescription, int max) {
mInfoDialog.onBugreportFinished();
BugreportInfo info = getInfo(id);
if (info == null) {
@@ -809,22 +822,17 @@
}
info.renameScreenshots(mScreenshotsDir);
info.bugreportFile = bugreportFile;
+ if (screenshotFile != null) {
+ info.addScreenshot(screenshotFile);
+ }
- final int max = intent.getIntExtra(EXTRA_MAX, -1);
if (max != -1) {
MetricsLogger.histogram(this, "dumpstate_duration", max);
info.max = max;
}
- final File screenshot = getFileExtra(intent, EXTRA_SCREENSHOT);
- if (screenshot != null) {
- info.addScreenshot(screenshot);
- }
-
- final String shareTitle = intent.getStringExtra(EXTRA_TITLE);
if (!TextUtils.isEmpty(shareTitle)) {
info.title = shareTitle;
- final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
if (!TextUtils.isEmpty(shareDescription)) {
info.shareDescription= shareDescription;
}
@@ -1944,6 +1952,23 @@
}
@Override
+ public void onProgress(int progress) throws RemoteException {
+ // TODO(b/111441001): change max argument?
+ updateProgressInfo(progress, CAPPED_MAX);
+ }
+
+ @Override
+ public void onError(int errorCode) throws RemoteException {
+ // TODO(b/111441001): implement
+ }
+
+ @Override
+ public void onFinished(long durationMs, String title, String description)
+ throws RemoteException {
+ // TODO(b/111441001): implement
+ }
+
+ @Override
public void onProgressUpdated(int progress) throws RemoteException {
/*
* Checks whether the progress changed in a way that should be displayed to the user:
@@ -1964,21 +1989,7 @@
}
if (newPercentage > oldPercentage) {
- if (DEBUG) {
- if (progress != info.progress) {
- Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id
- + ") from " + info.progress + " to " + progress);
- }
- if (max != info.max) {
- Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id
- + ") from " + info.max + " to " + max);
- }
- }
- info.progress = progress;
- info.max = max;
- info.lastUpdate = System.currentTimeMillis();
-
- updateProgress(info);
+ updateProgressInfo(progress, max);
}
}
@@ -2000,5 +2011,23 @@
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("token: "); pw.println(token);
}
+
+ private void updateProgressInfo(int progress, int max) {
+ if (DEBUG) {
+ if (progress != info.progress) {
+ Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id
+ + ") from " + info.progress + " to " + progress);
+ }
+ if (max != info.max) {
+ Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id
+ + ") from " + info.max + " to " + max);
+ }
+ }
+ info.progress = progress;
+ info.max = max;
+ info.lastUpdate = System.currentTimeMillis();
+
+ updateProgress(info);
+ }
}
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1c1a140..b4f2711 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -87,6 +87,7 @@
<uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
<uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
<uses-permission android:name="android.permission.START_ANY_ACTIVITY" />
+ <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 1539584..9a11b96 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -472,7 +472,8 @@
@Override
public void onActivityViewDestroyed(ActivityView view) {
- NotificationEntry entry = mExpandedBubble.getEntry();
+ NotificationEntry entry = mExpandedBubble != null
+ ? mExpandedBubble.getEntry() : null;
Log.d(TAG, "onActivityViewDestroyed(key="
+ ((entry != null) ? entry.key : "(none)") + "): " + view);
}
diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk
index 848f2bd..2fa1dde 100644
--- a/packages/WallpaperCropper/Android.mk
+++ b/packages/WallpaperCropper/Android.mk
@@ -8,6 +8,7 @@
LOCAL_PACKAGE_NAME := WallpaperCropper
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
diff --git a/packages/WallpaperCropper/CleanSpec.mk b/packages/WallpaperCropper/CleanSpec.mk
new file mode 100644
index 0000000..e6d8d5a
--- /dev/null
+++ b/packages/WallpaperCropper/CleanSpec.mk
@@ -0,0 +1,50 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/WallpaperCropper)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 5b45a08..44edb56 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6794,6 +6794,13 @@
// OS: Q
NOTIFICATION_BLOCKING_HELPER = 1621;
+ // ACTION: Tap & Pay -> Default Application Setting -> Use Forground
+ // OS: Q
+ ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622;
+
+ // ACTION: Tap & Pay -> Default Application Setting -> Use Default
+ // OS: Q
+ ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623;
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6cccd62..24fd7b9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
import static android.util.DebugUtils.flagsToString;
+import static android.view.autofill.AutofillManager.MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
@@ -101,8 +102,6 @@
private static final Object sLock = AutofillManagerService.class;
- private static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
-
/**
* IME supports Smart Suggestions.
*/
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 239a386..a8ff9b0 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -168,7 +168,7 @@
}
};
- // TODO(b/111330312): set cancellation signal, timeout (from both mClient and service),
+ // TODO(b/122728762): set cancellation signal, timeout (from both mClient and service),
// cache IAugmentedAutofillManagerClient reference, etc...
try {
mClient.getAugmentedAutofillClient(receiver);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0348f2b..cf4963c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2581,7 +2581,10 @@
final RemoteAugmentedAutofillService remoteService = mService
.getRemoteAugmentedAutofillServiceLocked();
- if (remoteService == null) return null;
+ if (remoteService == null) {
+ if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no service for user");
+ return null;
+ }
// Define which mode will be used
final int mode;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 12db4f3..ff378b3 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -149,6 +149,9 @@
if (userBackupManagerService != null) {
userBackupManagerService.tearDownService();
+
+ KeyValueBackupJob.cancel(userId, mContext);
+ FullBackupJob.cancel(userId, mContext);
}
}
@@ -577,9 +580,9 @@
* @return Whether ongoing work will continue. The return value here will be passed along as the
* return value to the callback {@link JobService#onStartJob(JobParameters)}.
*/
- public boolean beginFullBackup(FullBackupJob scheduledJob) {
+ public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "beginFullBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
return userBackupManagerService != null
&& userBackupManagerService.beginFullBackup(scheduledJob);
@@ -589,9 +592,9 @@
* Used by the {@link JobScheduler} to end the current full backup task when conditions are no
* longer met for running the full backup job.
*/
- public void endFullBackup() {
+ public void endFullBackup(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "endFullBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
if (userBackupManagerService != null) {
userBackupManagerService.endFullBackup();
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 5708b1c..33d21cb0 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -22,18 +22,30 @@
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
public class FullBackupJob extends JobService {
+ private static final String USER_ID_EXTRA_KEY = "userId";
+
+ @VisibleForTesting
+ static final int MIN_JOB_ID = 52418896;
+ @VisibleForTesting
+ static final int MAX_JOB_ID = 52419896;
+
private static ComponentName sIdleService =
new ComponentName("android", FullBackupJob.class.getName());
- private static final int JOB_ID = 0x5038;
+ @GuardedBy("mParamsForUser")
+ private final SparseArray<JobParameters> mParamsForUser = new SparseArray<>();
- private JobParameters mParams;
-
- public static void schedule(Context ctx, long minDelay, BackupManagerConstants constants) {
+ public static void schedule(int userId, Context ctx, long minDelay,
+ BackupManagerConstants constants) {
JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sIdleService);
+ JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId), sIdleService);
synchronized (constants) {
builder.setRequiresDeviceIdle(true)
.setRequiredNetworkType(constants.getFullBackupRequiredNetworkType())
@@ -42,14 +54,28 @@
if (minDelay > 0) {
builder.setMinimumLatency(minDelay);
}
+
+ Bundle extraInfo = new Bundle();
+ extraInfo.putInt(USER_ID_EXTRA_KEY, userId);
+ builder.setTransientExtras(extraInfo);
+
js.schedule(builder.build());
}
+ public static void cancel(int userId, Context ctx) {
+ JobScheduler js = (JobScheduler) ctx.getSystemService(
+ Context.JOB_SCHEDULER_SERVICE);
+ js.cancel(getJobIdForUserId(userId));
+ }
+
// callback from the Backup Manager Service: it's finished its work for this pass
- public void finishBackupPass() {
- if (mParams != null) {
- jobFinished(mParams, false);
- mParams = null;
+ public void finishBackupPass(int userId) {
+ synchronized (mParamsForUser) {
+ JobParameters jobParameters = mParamsForUser.get(userId);
+ if (jobParameters != null) {
+ jobFinished(jobParameters, false);
+ mParamsForUser.remove(userId);
+ }
}
}
@@ -57,19 +83,33 @@
@Override
public boolean onStartJob(JobParameters params) {
- mParams = params;
+ int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
+ synchronized (mParamsForUser) {
+ mParamsForUser.put(userId, params);
+ }
+
Trampoline service = BackupManagerService.getInstance();
- return service.beginFullBackup(this);
+ return service.beginFullBackup(userId, this);
}
@Override
public boolean onStopJob(JobParameters params) {
- if (mParams != null) {
- mParams = null;
- Trampoline service = BackupManagerService.getInstance();
- service.endFullBackup();
+ int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
+ synchronized (mParamsForUser) {
+ if (mParamsForUser.removeReturnOld(userId) == null) {
+ return false;
+ }
}
+
+ Trampoline service = BackupManagerService.getInstance();
+ service.endFullBackup(userId);
+
return false;
}
+ private static int getJobIdForUserId(int userId) {
+ return JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, userId);
+ }
}
diff --git a/services/backup/java/com/android/server/backup/JobIdManager.java b/services/backup/java/com/android/server/backup/JobIdManager.java
new file mode 100644
index 0000000..2e834dbf
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/JobIdManager.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+/**
+ * Allocates job IDs for {@link FullBackupJob} and {@link KeyValueBackupJob}
+ */
+public class JobIdManager {
+ public static int getJobIdForUserId(int minJobId, int maxJobId, int userId) {
+ if (minJobId + userId > maxJobId) {
+ throw new RuntimeException("No job IDs available in the given range");
+ }
+
+ return minJobId + userId;
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index f2e7435..d43859e 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -25,8 +25,14 @@
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.Random;
@@ -38,7 +44,8 @@
private static final String TAG = "KeyValueBackupJob";
private static ComponentName sKeyValueJobService =
new ComponentName("android", KeyValueBackupJob.class.getName());
- private static final int JOB_ID = 0x5039;
+
+ private static final String USER_ID_EXTRA_KEY = "userId";
// Once someone asks for a backup, this is how long we hold off until we find
// an on-charging opportunity. If we hit this max latency we will run the operation
@@ -46,16 +53,22 @@
// BackupManager.backupNow().
private static final long MAX_DEFERRAL = AlarmManager.INTERVAL_DAY;
- private static boolean sScheduled = false;
- private static long sNextScheduled = 0;
+ @GuardedBy("KeyValueBackupJob.class")
+ private static final SparseBooleanArray sScheduledForUserId = new SparseBooleanArray();
+ @GuardedBy("KeyValueBackupJob.class")
+ private static final SparseLongArray sNextScheduledForUserId = new SparseLongArray();
- public static void schedule(Context ctx, BackupManagerConstants constants) {
- schedule(ctx, 0, constants);
+ private static final int MIN_JOB_ID = 52417896;
+ private static final int MAX_JOB_ID = 52418896;
+
+ public static void schedule(int userId, Context ctx, BackupManagerConstants constants) {
+ schedule(userId, ctx, 0, constants);
}
- public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+ public static void schedule(int userId, Context ctx, long delay,
+ BackupManagerConstants constants) {
synchronized (KeyValueBackupJob.class) {
- if (sScheduled) {
+ if (sScheduledForUserId.get(userId)) {
return;
}
@@ -76,51 +89,61 @@
if (DEBUG_SCHEDULING) {
Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
}
- JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
+
+ JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId),
+ sKeyValueJobService)
.setMinimumLatency(delay)
.setRequiredNetworkType(networkType)
.setRequiresCharging(needsCharging)
.setOverrideDeadline(MAX_DEFERRAL);
+
+ Bundle extraInfo = new Bundle();
+ extraInfo.putInt(USER_ID_EXTRA_KEY, userId);
+ builder.setTransientExtras(extraInfo);
+
JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
js.schedule(builder.build());
- sNextScheduled = System.currentTimeMillis() + delay;
- sScheduled = true;
+ sScheduledForUserId.put(userId, true);
+ sNextScheduledForUserId.put(userId, System.currentTimeMillis() + delay);
}
}
- public static void cancel(Context ctx) {
+ public static void cancel(int userId, Context ctx) {
synchronized (KeyValueBackupJob.class) {
- JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- js.cancel(JOB_ID);
- sNextScheduled = 0;
- sScheduled = false;
+ JobScheduler js = (JobScheduler) ctx.getSystemService(
+ Context.JOB_SCHEDULER_SERVICE);
+ js.cancel(getJobIdForUserId(userId));
+
+ clearScheduledForUserId(userId);
}
}
- public static long nextScheduled() {
+ public static long nextScheduled(int userId) {
synchronized (KeyValueBackupJob.class) {
- return sNextScheduled;
+ return sNextScheduledForUserId.get(userId);
}
}
- public static boolean isScheduled() {
+ @VisibleForTesting
+ public static boolean isScheduled(int userId) {
synchronized (KeyValueBackupJob.class) {
- return sScheduled;
+ return sScheduledForUserId.get(userId);
}
}
@Override
public boolean onStartJob(JobParameters params) {
+ int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
synchronized (KeyValueBackupJob.class) {
- sNextScheduled = 0;
- sScheduled = false;
+ clearScheduledForUserId(userId);
}
// Time to run a key/value backup!
Trampoline service = BackupManagerService.getInstance();
try {
- service.backupNow();
+ service.backupNowForUser(userId);
} catch (RemoteException e) {}
// This was just a trigger; ongoing wakelock management is done by the
@@ -134,4 +157,13 @@
return false;
}
+ @GuardedBy("KeyValueBackupJob.class")
+ private static void clearScheduledForUserId(int userId) {
+ sScheduledForUserId.delete(userId);
+ sNextScheduledForUserId.delete(userId);
+ }
+
+ private static int getJobIdForUserId(int userId) {
+ return JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, userId);
+ }
}
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index e4ce62d..4a1e5b9 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -23,9 +23,9 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
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;
@@ -49,9 +49,8 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Set;
-
import java.util.Objects;
+import java.util.Set;
/**
* We back up the signatures of each package so that during a system restore,
@@ -95,6 +94,7 @@
// is coming from pre-Android P device.
private static final int UNDEFINED_ANCESTRAL_RECORD_VERSION = -1;
+ private int mUserId;
private List<PackageInfo> mAllPackages;
private PackageManager mPackageManager;
// version & signature info of each app in a restore set
@@ -129,17 +129,18 @@
// We're constructed with the set of applications that are participating
// in backup. This set changes as apps are installed & removed.
- public PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
- init(packageMgr, packages);
+ public PackageManagerBackupAgent(
+ PackageManager packageMgr, List<PackageInfo> packages, int userId) {
+ init(packageMgr, packages, userId);
}
- public PackageManagerBackupAgent(PackageManager packageMgr) {
- init(packageMgr, null);
+ public PackageManagerBackupAgent(PackageManager packageMgr, int userId) {
+ init(packageMgr, null, userId);
evaluateStorablePackages();
}
- private void init(PackageManager packageMgr, List<PackageInfo> packages) {
+ private void init(PackageManager packageMgr, List<PackageInfo> packages, int userId) {
mPackageManager = packageMgr;
mAllPackages = packages;
mRestoredSignatures = null;
@@ -147,17 +148,19 @@
mStoredSdkVersion = Build.VERSION.SDK_INT;
mStoredIncrementalVersion = Build.VERSION.INCREMENTAL;
+ mUserId = userId;
}
// We will need to refresh our understanding of what is eligible for
// backup periodically; this entry point serves that purpose.
public void evaluateStorablePackages() {
- mAllPackages = getStorableApplications(mPackageManager);
+ mAllPackages = getStorableApplications(mPackageManager, mUserId);
}
- public static List<PackageInfo> getStorableApplications(PackageManager pm) {
- List<PackageInfo> pkgs;
- pkgs = pm.getInstalledPackages(PackageManager.GET_SIGNING_CERTIFICATES);
+ /** Gets all packages installed on user {@code userId} eligible for backup. */
+ public static List<PackageInfo> getStorableApplications(PackageManager pm, int userId) {
+ List<PackageInfo> pkgs =
+ pm.getInstalledPackagesAsUser(PackageManager.GET_SIGNING_CERTIFICATES, userId);
int N = pkgs.size();
for (int a = N-1; a >= 0; a--) {
PackageInfo pkg = pkgs.get(a);
@@ -237,8 +240,8 @@
ComponentName home = getPreferredHomeComponent();
if (home != null) {
try {
- homeInfo = mPackageManager.getPackageInfo(home.getPackageName(),
- PackageManager.GET_SIGNING_CERTIFICATES);
+ homeInfo = mPackageManager.getPackageInfoAsUser(home.getPackageName(),
+ PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
homeVersion = homeInfo.getLongVersionCode();
SigningInfo signingInfo = homeInfo.signingInfo;
@@ -315,8 +318,8 @@
} else {
PackageInfo info = null;
try {
- info = mPackageManager.getPackageInfo(packName,
- PackageManager.GET_SIGNING_CERTIFICATES);
+ info = mPackageManager.getPackageInfoAsUser(packName,
+ PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
} catch (NameNotFoundException e) {
// Weird; we just found it, and now are told it doesn't exist.
// Treat it as having been removed from the device.
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 79d4a2c..4ca2545 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -698,15 +698,15 @@
// Full backup/restore entry points - non-Binder; called directly
// by the full-backup scheduled job
- /* package */ boolean beginFullBackup(FullBackupJob scheduledJob) {
+ /* package */ boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.beginFullBackup(scheduledJob) : false;
+ return (svc != null) ? svc.beginFullBackup(userId, scheduledJob) : false;
}
- /* package */ void endFullBackup() {
+ /* package */ void endFullBackup(@UserIdInt int userId) {
BackupManagerService svc = mService;
if (svc != null) {
- svc.endFullBackup();
+ svc.endFullBackup(userId);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index e075785..3a8966a 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -249,11 +249,11 @@
private final TransportManager mTransportManager;
private final HandlerThread mUserBackupThread;
- private Context mContext;
- private PackageManager mPackageManager;
- private IPackageManager mPackageManagerBinder;
- private IActivityManager mActivityManager;
- private ActivityManagerInternal mActivityManagerInternal;
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+ private final IPackageManager mPackageManagerBinder;
+ private final IActivityManager mActivityManager;
+ private final ActivityManagerInternal mActivityManagerInternal;
private PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
private final IStorageManager mStorageManager;
@@ -496,11 +496,18 @@
mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null");
mBaseStateDir.mkdirs();
if (!SELinux.restorecon(mBaseStateDir)) {
- Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+ Slog.w(TAG, "SELinux restorecon failed on " + mBaseStateDir);
}
mDataDir = checkNotNull(dataDir, "dataDir cannot be null");
-
+ // TODO(b/120424138): Remove when the system user moves out of the cache dir. The cache dir
+ // is managed by init.rc so we don't have to create it below.
+ if (userId != UserHandle.USER_SYSTEM) {
+ mDataDir.mkdirs();
+ if (!SELinux.restorecon(mDataDir)) {
+ Slog.w(TAG, "SELinux restorecon failed on " + mDataDir);
+ }
+ }
mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
// Receivers for scheduled backups and transport initialization operations.
@@ -797,7 +804,7 @@
* non-lifecycle agent instance, so we manually set up the context topology for it.
*/
public BackupAgent makeMetadataAgent() {
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager, mUserId);
pmAgent.attach(mContext);
pmAgent.onCreate();
return pmAgent;
@@ -808,7 +815,7 @@
*/
public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
PackageManagerBackupAgent pmAgent =
- new PackageManagerBackupAgent(mPackageManager, packages);
+ new PackageManagerBackupAgent(mPackageManager, packages, mUserId);
pmAgent.attach(mContext);
pmAgent.onCreate();
return pmAgent;
@@ -879,7 +886,7 @@
boolean changed = false;
ArrayList<FullBackupEntry> schedule = null;
List<PackageInfo> apps =
- PackageManagerBackupAgent.getStorableApplications(mPackageManager);
+ PackageManagerBackupAgent.getStorableApplications(mPackageManager, mUserId);
if (mFullBackupScheduleFile.exists()) {
try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
@@ -1428,8 +1435,7 @@
mConnecting = true;
mConnectedAgent = null;
try {
- if (mActivityManager.bindBackupAgent(app.packageName, mode,
- UserHandle.USER_OWNER)) {
+ if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId)) {
Slog.d(TAG, "awaiting agent for " + app);
// success; wait for the agent to arrive
@@ -1460,11 +1466,7 @@
}
}
if (agent == null) {
- try {
- mActivityManager.clearPendingBackup();
- } catch (RemoteException e) {
- // can't happen - ActivityManager is local
- }
+ mActivityManagerInternal.clearPendingBackup(mUserId);
}
return agent;
}
@@ -1488,7 +1490,7 @@
public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
// Don't wipe packages marked allowClearUserData=false
try {
- PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+ PackageInfo info = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
if (MORE_DEBUG) {
Slog.i(TAG, "allowClearUserData=false so not wiping "
@@ -1507,7 +1509,7 @@
mClearingData = true;
try {
mActivityManager.clearApplicationUserData(
- packageName, keepSystemState, observer, 0);
+ packageName, keepSystemState, observer, mUserId);
} catch (RemoteException e) {
// can't happen because the activity manager is in this process
}
@@ -1616,8 +1618,8 @@
continue;
}
try {
- PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES);
+ PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
mPackageManager)) {
BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
@@ -1674,8 +1676,8 @@
}
// We don't want the backup jobs to kick in any time soon.
// Reschedules them to run in the distant future.
- KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
- FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
+ KeyValueBackupJob.schedule(mUserId, mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+ FullBackupJob.schedule(mUserId, mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
} finally {
Binder.restoreCallingIdentity(oldToken);
}
@@ -1910,7 +1912,7 @@
Runnable r = new Runnable() {
@Override
public void run() {
- FullBackupJob.schedule(mContext, latency, mConstants);
+ FullBackupJob.schedule(mUserId, mContext, latency, mConstants);
}
};
mBackupHandler.postDelayed(r, 2500);
@@ -2033,7 +2035,7 @@
mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
if (result.batterySaverEnabled) {
if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
- FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
+ FullBackupJob.schedule(mUserId, mContext, keyValueBackupInterval, mConstants);
return false;
}
@@ -2147,7 +2149,7 @@
mBackupHandler.post(new Runnable() {
@Override
public void run() {
- FullBackupJob.schedule(mContext, deferTime, mConstants);
+ FullBackupJob.schedule(mUserId, mContext, deferTime, mConstants);
}
});
return false;
@@ -2251,7 +2253,7 @@
}
// ...and schedule a backup pass if necessary
- KeyValueBackupJob.schedule(mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
}
// Note: packageName is currently unused, but may be in the future
@@ -2339,8 +2341,8 @@
if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
PackageInfo info;
try {
- info = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES);
+ info = mPackageManager.getPackageInfoAsUser(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
} catch (NameNotFoundException e) {
Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
return;
@@ -2401,7 +2403,8 @@
mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
if (result.batterySaverEnabled) {
if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
- KeyValueBackupJob.schedule(mContext, mConstants); // try again in several hours
+ // Try again in several hours.
+ KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
} else {
if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
synchronized (mQueueLock) {
@@ -2414,7 +2417,7 @@
}
// ...and cancel any pending scheduled job, because we've just superseded it
- KeyValueBackupJob.cancel(mContext);
+ KeyValueBackupJob.cancel(mUserId, mContext);
}
}
} finally {
@@ -2737,13 +2740,13 @@
synchronized (mQueueLock) {
if (enable && !wasEnabled && mSetupComplete) {
// if we've just been enabled, start scheduling backup passes
- KeyValueBackupJob.schedule(mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
scheduleNextFullBackupJob(0);
} else if (!enable) {
// No longer enabled, so stop running backups
if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
- KeyValueBackupJob.cancel(mContext);
+ KeyValueBackupJob.cancel(mUserId, mContext);
// This also constitutes an opt-out, so we wipe any data for
// this device from the backend. We start that process with
@@ -3257,7 +3260,7 @@
if (packageName != null) {
PackageInfo app = null;
try {
- app = mPackageManager.getPackageInfo(packageName, 0);
+ app = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
} catch (NameNotFoundException nnf) {
Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
throw new IllegalArgumentException("Package " + packageName + " not found");
@@ -3361,7 +3364,7 @@
mTransportManager.getCurrentTransportClient(callerLogString);
boolean eligible =
AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager);
+ transportClient, packageName, mPackageManager, mUserId);
if (transportClient != null) {
mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
}
@@ -3385,7 +3388,7 @@
for (String packageName : packages) {
if (AppBackupUtils
.appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager)) {
+ transportClient, packageName, mPackageManager, mUserId)) {
eligibleApps.add(packageName);
}
}
@@ -3451,7 +3454,7 @@
pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
pw.println("Last backup pass started: " + mLastBackupPass
+ " (now = " + System.currentTimeMillis() + ')');
- pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled());
+ pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId));
pw.println("Transport whitelist:");
for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 2ee96d1..0fb4f93 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -633,7 +633,7 @@
unregisterTask();
if (mJob != null) {
- mJob.finishBackupPass();
+ mJob.finishBackupPass(backupManagerService.getUserId());
}
synchronized (backupManagerService.getQueueLock()) {
diff --git a/services/backup/java/com/android/server/backup/internal/SetupObserver.java b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
index 62ae3eb..c5e912e 100644
--- a/services/backup/java/com/android/server/backup/internal/SetupObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
@@ -77,7 +77,8 @@
if (MORE_DEBUG) {
Slog.d(TAG, "Setup complete so starting backups");
}
- KeyValueBackupJob.schedule(mContext, mUserBackupManagerService.getConstants());
+ KeyValueBackupJob.schedule(mUserBackupManagerService.getUserId(), mContext,
+ mUserBackupManagerService.getConstants());
mUserBackupManagerService.scheduleNextFullBackupJob(0);
}
}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index f39d795..862ca71 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -45,7 +45,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SELinux;
-import android.os.UserHandle;
import android.os.WorkSource;
import com.android.internal.annotations.GuardedBy;
@@ -241,6 +240,7 @@
private final boolean mUserInitiated;
private final boolean mNonIncremental;
private final int mCurrentOpToken;
+ private final int mUserId;
private final File mStateDirectory;
private final File mDataDirectory;
private final File mBlankStateFile;
@@ -320,6 +320,7 @@
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mQueueLock = mBackupManagerService.getQueueLock();
mBlankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME);
+ mUserId = backupManagerService.getUserId();
}
private void registerTask() {
@@ -480,8 +481,8 @@
final PackageInfo packageInfo;
try {
packageInfo =
- mPackageManager.getPackageInfo(
- packageName, PackageManager.GET_SIGNING_CERTIFICATES);
+ mPackageManager.getPackageInfoAsUser(
+ packageName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
} catch (PackageManager.NameNotFoundException e) {
mReporter.onAgentUnknown(packageName);
throw AgentException.permanent(e);
@@ -770,8 +771,7 @@
private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
throws IOException {
- // TODO: http://b/22388012
- byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, UserHandle.USER_SYSTEM);
+ byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, mUserId);
File widgetFile = new File(mStateDirectory, pkgName + "_widget");
boolean priorStateExists = widgetFile.exists();
if (!priorStateExists && widgetState == null) {
@@ -1003,7 +1003,7 @@
// Use the scheduler's default.
delay = 0;
}
- KeyValueBackupJob.schedule(
+ KeyValueBackupJob.schedule(mBackupManagerService.getUserId(),
mBackupManagerService.getContext(), delay, mBackupManagerService.getConstants());
for (String packageName : mOriginalQueue) {
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index e273b32..0fa0f89 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -54,6 +54,7 @@
private final TransportManager mTransportManager;
private final String mTransportName;
private final UserBackupManagerService mBackupManagerService;
+ private final int mUserId;
@Nullable private final String mPackageName;
public RestoreSet[] mRestoreSets = null;
boolean mEnded = false;
@@ -67,6 +68,7 @@
mPackageName = packageName;
mTransportManager = backupManagerService.getTransportManager();
mTransportName = transportName;
+ mUserId = backupManagerService.getUserId();
}
public void markTimedOut() {
@@ -304,7 +306,8 @@
final PackageInfo app;
try {
- app = mBackupManagerService.getPackageManager().getPackageInfo(packageName, 0);
+ app = mBackupManagerService.getPackageManager().getPackageInfoAsUser(
+ packageName, 0, mUserId);
} catch (NameNotFoundException nnf) {
Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
return -1;
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 45a398f..c7f3315 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -272,7 +272,7 @@
instream, mBackupManagerService.getContext(),
mDeleteObserver, mManifestSignatures,
mPackagePolicies, info, installerPackageName,
- bytesReadListener);
+ bytesReadListener, mBackupManagerService.getUserId());
// good to go; promote to ACCEPT
mPackagePolicies.put(pkg, isSuccessfullyInstalled
? RestorePolicy.ACCEPT
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index f7efad6..5284d94 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -48,7 +48,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
@@ -81,6 +80,7 @@
public class PerformUnifiedRestoreTask implements BackupRestoreTask {
private UserBackupManagerService backupManagerService;
+ private final int mUserId;
private final TransportManager mTransportManager;
// Transport client we're working with to do the restore
private final TransportClient mTransportClient;
@@ -175,6 +175,7 @@
@Nullable String[] filterSet,
OnTaskFinishedListener listener) {
this.backupManagerService = backupManagerService;
+ mUserId = backupManagerService.getUserId();
mTransportManager = backupManagerService.getTransportManager();
mEphemeralOpToken = backupManagerService.generateRandomIntegerToken();
mState = UnifiedRestoreState.INITIAL;
@@ -204,7 +205,7 @@
// We want everything and a pony
List<PackageInfo> apps =
PackageManagerBackupAgent.getStorableApplications(
- backupManagerService.getPackageManager());
+ backupManagerService.getPackageManager(), mUserId);
filterSet = packagesToNames(apps);
if (DEBUG) {
Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
@@ -221,7 +222,7 @@
for (int i = 0; i < filterSet.length; i++) {
try {
PackageManager pm = backupManagerService.getPackageManager();
- PackageInfo info = pm.getPackageInfo(filterSet[i], 0);
+ PackageInfo info = pm.getPackageInfoAsUser(filterSet[i], 0, mUserId);
if ("android".equals(info.packageName)) {
hasSystem = true;
continue;
@@ -240,16 +241,16 @@
}
if (hasSystem) {
try {
- mAcceptSet.add(0,
- backupManagerService.getPackageManager().getPackageInfo("android", 0));
+ mAcceptSet.add(0, backupManagerService.getPackageManager().getPackageInfoAsUser(
+ "android", 0, mUserId));
} catch (NameNotFoundException e) {
// won't happen; we know a priori that it's valid
}
}
if (hasSettings) {
try {
- mAcceptSet.add(backupManagerService.getPackageManager().getPackageInfo(
- SETTINGS_PACKAGE, 0));
+ mAcceptSet.add(backupManagerService.getPackageManager().getPackageInfoAsUser(
+ SETTINGS_PACKAGE, 0, mUserId));
} catch (NameNotFoundException e) {
// this one is always valid too
}
@@ -360,7 +361,7 @@
// If we're starting a full-system restore, set up to begin widget ID remapping
if (mIsSystemRestore) {
// TODO: http://b/22388012
- AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM);
+ AppWidgetBackupBridge.restoreStarting(mUserId);
}
try {
@@ -508,8 +509,8 @@
}
try {
- mCurrentPackage = backupManagerService.getPackageManager().getPackageInfo(
- pkgName, PackageManager.GET_SIGNING_CERTIFICATES);
+ mCurrentPackage = backupManagerService.getPackageManager().getPackageInfoAsUser(
+ pkgName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
} catch (NameNotFoundException e) {
// Whoops, we thought we could restore this package but it
// turns out not to be present. Skip it.
@@ -1079,7 +1080,7 @@
// Kick off any work that may be needed regarding app widget restores
// TODO: http://b/22388012
- AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM);
+ AppWidgetBackupBridge.restoreFinished(mUserId);
// If this was a full-system restore, record the ancestral
// dataset information
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 e465c7e..054879b 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -91,10 +91,13 @@
* </ol>
*/
public static boolean appIsRunningAndEligibleForBackupWithTransport(
- @Nullable TransportClient transportClient, String packageName, PackageManager pm) {
+ @Nullable TransportClient transportClient,
+ String packageName,
+ PackageManager pm,
+ int userId) {
try {
- PackageInfo packageInfo = pm.getPackageInfo(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES);
+ PackageInfo packageInfo = pm.getPackageInfoAsUser(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES, userId);
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
if (!appIsEligibleForBackup(applicationInfo, pm)
|| appIsStopped(applicationInfo)
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index df7e6d4..cce5b3b 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -72,7 +72,9 @@
HashMap<String, Signature[]> manifestSignatures,
HashMap<String, RestorePolicy> packagePolicies,
FileMetadata info,
- String installerPackageName, BytesReadListener bytesReadListener) {
+ String installerPackageName,
+ BytesReadListener bytesReadListener,
+ int userId) {
boolean okay = true;
if (DEBUG) {
@@ -144,8 +146,8 @@
uninstall = true;
} else {
try {
- PackageInfo pkg = packageManager.getPackageInfo(info.packageName,
- PackageManager.GET_SIGNING_CERTIFICATES);
+ PackageInfo pkg = packageManager.getPackageInfoAsUser(info.packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES, userId);
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP)
== 0) {
Slog.w(TAG, "Restore stream contains apk of package "
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3b08a00..bd6fa49 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -920,8 +920,8 @@
/**
* Backup/restore process management
*/
- String mBackupAppName = null;
- BackupRecord mBackupTarget = null;
+ @GuardedBy("this")
+ final SparseArray<BackupRecord> mBackupTargets = new SparseArray<>();
final ProviderMap mProviderMap;
@@ -4365,6 +4365,7 @@
mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
}
+ @GuardedBy("this")
private final void processStartTimedOutLocked(ProcessRecord app) {
final int pid = app.pid;
boolean gone = mPidsSelfLocked.removeIfNoThread(pid);
@@ -4385,7 +4386,8 @@
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
removeLruProcessLocked(app);
- if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
+ final BackupRecord backupTarget = mBackupTargets.get(app.userId);
+ if (backupTarget != null && backupTarget.app.pid == pid) {
Slog.w(TAG, "Unattached app died before backup, skipping");
mHandler.post(new Runnable() {
@Override
@@ -4393,7 +4395,7 @@
try {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentDisconnected(app.info.packageName);
+ bm.agentDisconnectedForUser(app.userId, app.info.packageName);
} catch (RemoteException e) {
// Can't happen; the backup manager is local
}
@@ -4516,6 +4518,7 @@
if (DEBUG_ALL) Slog.v(
TAG, "New app record " + app
+ " thread=" + thread.asBinder() + " pid=" + pid);
+ final BackupRecord backupTarget = mBackupTargets.get(app.userId);
try {
int testMode = ApplicationThreadConstants.DEBUG_OFF;
if (mDebugApp != null && mDebugApp.equals(processName)) {
@@ -4537,11 +4540,11 @@
// If the app is being launched for restore or full backup, set it up specially
boolean isRestrictedBackupMode = false;
- if (mBackupTarget != null && mBackupAppName.equals(processName)) {
- isRestrictedBackupMode = mBackupTarget.appInfo.uid >= FIRST_APPLICATION_UID
- && ((mBackupTarget.backupMode == BackupRecord.RESTORE)
- || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
- || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
+ if (backupTarget != null && backupTarget.appInfo.packageName.equals(processName)) {
+ isRestrictedBackupMode = backupTarget.appInfo.uid >= FIRST_APPLICATION_UID
+ && ((backupTarget.backupMode == BackupRecord.RESTORE)
+ || (backupTarget.backupMode == BackupRecord.RESTORE_FULL)
+ || (backupTarget.backupMode == BackupRecord.BACKUP_FULL));
}
final ActiveInstrumentation instr = app.getActiveInstrumentation();
@@ -4749,15 +4752,15 @@
}
// Check whether the next backup agent is in this process...
- if (!badApp && mBackupTarget != null && mBackupTarget.app == app) {
+ if (!badApp && backupTarget != null && backupTarget.app == app) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
"New app is backup target, launching agent for " + app);
- notifyPackageUse(mBackupTarget.appInfo.packageName,
+ notifyPackageUse(backupTarget.appInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
try {
- thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
- compatibilityInfoForPackage(mBackupTarget.appInfo),
- mBackupTarget.backupMode);
+ thread.scheduleCreateBackupAgent(backupTarget.appInfo,
+ compatibilityInfoForPackage(backupTarget.appInfo),
+ backupTarget.backupMode);
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
badApp = true;
@@ -13224,16 +13227,17 @@
app.receivers.clear();
// If the app is undergoing backup, tell the backup manager about it
- if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
+ final BackupRecord backupTarget = mBackupTargets.get(app.userId);
+ if (backupTarget != null && app.pid == backupTarget.app.pid) {
if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
- + mBackupTarget.appInfo + " died during backup");
+ + backupTarget.appInfo + " died during backup");
mHandler.post(new Runnable() {
@Override
public void run(){
try {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentDisconnected(app.info.packageName);
+ bm.agentDisconnectedForUser(app.userId, app.info.packageName);
} catch (RemoteException e) {
// can't happen; backup manager is local
}
@@ -13573,7 +13577,11 @@
// instantiated. The backup agent will invoke backupAgentCreated() on the
// activity manager to announce its creation.
public boolean bindBackupAgent(String packageName, int backupMode, int userId) {
- if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode);
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode="
+ + backupMode + " userId=" + userId + " callingUid = " + Binder.getCallingUid()
+ + " uid = " + Process.myUid());
+ }
enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "bindBackupAgent");
IPackageManager pm = AppGlobals.getPackageManager();
@@ -13625,10 +13633,10 @@
proc.inFullBackup = true;
}
r.app = proc;
- oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1;
+ final BackupRecord backupTarget = mBackupTargets.get(userId);
+ oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
newBackupUid = proc.inFullBackup ? r.appInfo.uid : -1;
- mBackupTarget = r;
- mBackupAppName = app.packageName;
+ mBackupTargets.put(userId, r);
// Try not to kill the process during backup
updateOomAdjLocked(proc, true);
@@ -13663,14 +13671,14 @@
return true;
}
- @Override
- public void clearPendingBackup() {
- if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "clearPendingBackup");
- enforceCallingPermission("android.permission.BACKUP", "clearPendingBackup");
+ private void clearPendingBackup(int userId) {
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG_BACKUP, "clearPendingBackup: userId = " + userId + " callingUid = "
+ + Binder.getCallingUid() + " uid = " + Process.myUid());
+ }
synchronized (this) {
- mBackupTarget = null;
- mBackupAppName = null;
+ mBackupTargets.delete(userId);
}
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
@@ -13678,12 +13686,19 @@
}
// A backup agent has just come up
+ @Override
public void backupAgentCreated(String agentPackageName, IBinder agent) {
- if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName
- + " = " + agent);
+ final int callingUserId = UserHandle.getCallingUserId();
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName + " = " + agent
+ + " callingUserId = " + callingUserId + " callingUid = "
+ + Binder.getCallingUid() + " uid = " + Process.myUid());
+ }
synchronized(this) {
- if (!agentPackageName.equals(mBackupAppName)) {
+ final BackupRecord backupTarget = mBackupTargets.get(callingUserId);
+ String backupAppName = backupTarget == null ? null : backupTarget.appInfo.packageName;
+ if (!agentPackageName.equals(backupAppName)) {
Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!");
return;
}
@@ -13693,7 +13708,7 @@
try {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentConnected(agentPackageName, agent);
+ bm.agentConnectedForUser(callingUserId, agentPackageName, agent);
} catch (RemoteException e) {
// can't happen; the backup manager service is local
} catch (Exception e) {
@@ -13706,7 +13721,12 @@
// done with this agent
public void unbindBackupAgent(ApplicationInfo appInfo) {
- if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo);
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo + " appInfo.uid = "
+ + appInfo.uid + " callingUid = " + Binder.getCallingUid() + " uid = "
+ + Process.myUid());
+ }
+
enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "unbindBackupAgent");
if (appInfo == null) {
Slog.w(TAG, "unbind backup agent for null app");
@@ -13715,24 +13735,27 @@
int oldBackupUid;
+ final int userId = UserHandle.getUserId(appInfo.uid);
synchronized(this) {
+ final BackupRecord backupTarget = mBackupTargets.get(userId);
+ String backupAppName = backupTarget == null ? null : backupTarget.appInfo.packageName;
try {
- if (mBackupAppName == null) {
+ if (backupAppName == null) {
Slog.w(TAG, "Unbinding backup agent with no active backup");
return;
}
- if (!mBackupAppName.equals(appInfo.packageName)) {
+ if (!backupAppName.equals(appInfo.packageName)) {
Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
return;
}
// Not backing this app up any more; reset its OOM adjustment
- final ProcessRecord proc = mBackupTarget.app;
+ final ProcessRecord proc = backupTarget.app;
updateOomAdjLocked(proc, true);
proc.inFullBackup = false;
- oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1;
+ oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
// If the app crashed during backup, 'thread' will be null here
if (proc.thread != null) {
@@ -13745,8 +13768,7 @@
}
}
} finally {
- mBackupTarget = null;
- mBackupAppName = null;
+ mBackupTargets.delete(userId);
}
}
@@ -17771,6 +17793,11 @@
public boolean isAppForeground(int uid) {
return ActivityManagerService.this.isAppForeground(uid);
}
+
+ @Override
+ public void clearPendingBackup(int userId) {
+ ActivityManagerService.this.clearPendingBackup(userId);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index cb4cac9..1f9362e 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1024,7 +1024,8 @@
app.hasStartedServices = false;
app.adjSeq = mAdjSeq;
- if (mService.mBackupTarget != null && app == mService.mBackupTarget.app) {
+ final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId);
+ if (backupTarget != null && app == backupTarget.app) {
// If possible we want to avoid killing apps while they're being backed up
if (adj > ProcessList.BACKUP_APP_ADJ) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 8d7811f..714a807 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -1454,8 +1454,8 @@
if (accessCount > 0) {
if (!printedUidState) {
mWriter.print(mUidStatePrefix);
- mWriter.print(AppOpsManager.uidStateToString(uidState));
- mWriter.print("[");
+ mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+ mWriter.print(" = ");
printedUidState = true;
}
mWriter.print("access=");
@@ -1465,11 +1465,11 @@
if (rejectCount > 0) {
if (!printedUidState) {
mWriter.print(mUidStatePrefix);
- mWriter.print(AppOpsManager.uidStateToString(uidState));
- mWriter.print("[");
+ mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+ mWriter.print(" = ");
printedUidState = true;
} else {
- mWriter.print(",");
+ mWriter.print(", ");
}
mWriter.print("reject=");
mWriter.print(rejectCount);
@@ -1478,16 +1478,17 @@
if (accessDuration > 0) {
if (!printedUidState) {
mWriter.print(mUidStatePrefix);
- mWriter.print(AppOpsManager.uidStateToString(uidState));
+ mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+ mWriter.print(" = ");
printedUidState = true;
} else {
- mWriter.print(",");
+ mWriter.print(", ");
}
mWriter.print("duration=");
- mWriter.print(accessDuration);
+ TimeUtils.formatDuration(accessDuration, mWriter);
}
if (printedUidState) {
- mWriter.println("]");
+ mWriter.println("");
}
}
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index b1e9144..b145e1e 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -106,9 +106,7 @@
synchronized (mProxyByGroupTmp) {
// record individual ranking result and nominate proxies for each group
- // Note: iteration is done backwards such that the index can be used as a sort key
- // in a string compare below
- for (int i = N - 1; i >= 0; i--) {
+ for (int i = 0; i < N; i++) {
final NotificationRecord record = notificationList.get(i);
record.setAuthoritativeRank(i);
final String groupKey = record.getGroupKey();
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index eb3017d..b6dae19 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -304,6 +304,17 @@
}
@Override
+ public boolean shouldHideFromSuggestions(String packageName, UserHandle user) {
+ if (!canAccessProfile(user.getIdentifier(), "cannot get shouldHideFromSuggestions")) {
+ return false;
+ }
+ final PackageManagerInternal pmi = LocalServices.getService(
+ PackageManagerInternal.class);
+ int flags = pmi.getDistractingPackageRestrictions(packageName, user.getIdentifier());
+ return (flags & PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS) != 0;
+ }
+
+ @Override
public ParceledListSlice<ResolveInfo> getLauncherActivities(String callingPackage,
String packageName, UserHandle user) throws RemoteException {
ParceledListSlice<ResolveInfo> launcherActivities = queryActivitiesForUser(
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index d0b20e8..bf4e272 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -225,6 +225,17 @@
Slog.w(TAG, "Deleting orphan icon " + icon);
icon.delete();
}
+
+ // Invalid sessions might have been marked while parsing. Re-write the database with
+ // the updated information.
+ writeSessionsLocked();
+
+ for (int i = 0; i < mSessions.size(); i++) {
+ final PackageInstallerSession session = mSessions.valueAt(i);
+ if (session.isStaged()) {
+ mStagingManager.restoreSession(session);
+ }
+ }
}
}
@@ -329,9 +340,6 @@
if (valid) {
mSessions.put(session.sessionId, session);
- if (session.isStaged()) {
- mStagingManager.restoreSession(session);
- }
} else {
// Since this is early during boot we don't send
// any observer events about the session, but we
@@ -1122,6 +1130,11 @@
mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
}
+ public void onStagedSessionChanged(PackageInstallerSession session) {
+ writeSessionsAsync();
+ mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId);
+ }
+
public void onSessionFinished(final PackageInstallerSession session, boolean success) {
mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 516927f..12d335d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2009,6 +2009,7 @@
mCallback.onSessionFinished(this, success);
}
+ /** {@hide} */
void setStagedSessionReady() {
synchronized (mLock) {
mStagedSessionReady = true;
@@ -2016,6 +2017,7 @@
mStagedSessionFailed = false;
mStagedSessionErrorCode = SessionInfo.NO_ERROR;
}
+ mCallback.onStagedSessionChanged(this);
}
/** {@hide} */
@@ -2026,6 +2028,33 @@
mStagedSessionFailed = true;
mStagedSessionErrorCode = errorCode;
}
+ mCallback.onStagedSessionChanged(this);
+ }
+
+ /** {@hide} */
+ void setStagedSessionApplied() {
+ synchronized (mLock) {
+ mStagedSessionReady = false;
+ mStagedSessionApplied = true;
+ mStagedSessionFailed = false;
+ mStagedSessionErrorCode = SessionInfo.NO_ERROR;
+ }
+ mCallback.onStagedSessionChanged(this);
+ }
+
+ /** {@hide} */
+ boolean isStagedSessionReady() {
+ return mStagedSessionReady;
+ }
+
+ /** {@hide} */
+ boolean isStagedSessionApplied() {
+ return mStagedSessionApplied;
+ }
+
+ /** {@hide} */
+ boolean isStagedSessionFailed() {
+ return mStagedSessionFailed;
}
private void destroyInternal() {
@@ -2221,6 +2250,16 @@
return permissionsArray;
}
+ // Sanity check to be performed when the session is restored from an external file. Only one
+ // of the session states should be true, or none of them.
+ private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied,
+ boolean isFailed) {
+ return (!isReady && !isApplied && !isFailed)
+ || (isReady && !isApplied && !isFailed)
+ || (!isReady && isApplied && !isFailed)
+ || (!isReady && !isApplied && isFailed);
+ }
+
/**
* Read new session from a {@link XmlPullParser xml description} and create it.
*
@@ -2287,6 +2326,10 @@
final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED);
final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE);
+ if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
+ throw new IllegalArgumentException("Can't restore staged session with invalid state.");
+ }
+
return new PackageInstallerSession(callback, context, pm, sessionProvider,
installerThread, stagingManager, sessionId, userId, installerPackageName,
installerUid, params, createdMillis, stageDir, stageCid, prepared, sealed,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 597f5b3..945d7ad 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -84,6 +84,7 @@
import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.RESTRICTION_NONE;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -12657,22 +12658,30 @@
info.sendPackageRemovedBroadcasts(true /*killApp*/);
}
+ private void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId,
+ int distractionFlags) {
+ final Bundle extras = new Bundle(3);
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+ extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
+ sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras,
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null);
+ }
+
private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
boolean suspended, PersistableBundle launcherExtras) {
- if (pkgList.length > 0) {
- Bundle extras = new Bundle(1);
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
- if (launcherExtras != null) {
- extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS,
- new Bundle(launcherExtras.deepCopy()));
- }
- sendPackageBroadcast(
- suspended ? Intent.ACTION_PACKAGES_SUSPENDED
- : Intent.ACTION_PACKAGES_UNSUSPENDED,
- null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
- new int[] {userId}, null);
+ final Bundle extras = new Bundle(3);
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+ if (launcherExtras != null) {
+ extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS,
+ new Bundle(launcherExtras.deepCopy()));
}
+ sendPackageBroadcast(
+ suspended ? Intent.ACTION_PACKAGES_SUSPENDED
+ : Intent.ACTION_PACKAGES_UNSUSPENDED,
+ null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
+ new int[] {userId}, null);
}
/**
@@ -12822,6 +12831,62 @@
}
@Override
+ public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
+ int restrictionFlags, int userId) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
+ "setPackagesSuspendedAsUser");
+
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
+ && UserHandle.getUserId(callingUid) != userId) {
+ throw new SecurityException("Calling uid " + callingUid + " cannot call for user "
+ + userId);
+ }
+ Preconditions.checkNotNull(packageNames, "packageNames cannot be null");
+
+ final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
+ final IntArray changedUids = new IntArray(packageNames.length);
+ final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
+
+ for (int i = 0; i < packageNames.length; i++) {
+ final String packageName = packageNames[i];
+ final PackageSetting pkgSetting;
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
+ Slog.w(TAG, "Could not find package setting for package: " + packageName
+ + ". Skipping...");
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ }
+ if (restrictionFlags != 0 && !canSuspendPackageForUserInternal(packageName, userId)) {
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ synchronized (mPackages) {
+ final int oldDistractionFlags = pkgSetting.getDistractionFlags(userId);
+ if (restrictionFlags != oldDistractionFlags) {
+ pkgSetting.setDistractionFlags(restrictionFlags, userId);
+ changedPackagesList.add(packageName);
+ changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+ }
+ }
+ }
+
+ if (!changedPackagesList.isEmpty()) {
+ final String[] changedPackages = changedPackagesList.toArray(
+ new String[changedPackagesList.size()]);
+ sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
+ restrictionFlags);
+ synchronized (mPackages) {
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
+ }
+ return unactionedPackages.toArray(new String[0]);
+ }
+
+ @Override
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras,
SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
@@ -12846,44 +12911,37 @@
final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
final IntArray changedUids = new IntArray(packageNames.length);
final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
- final long callingId = Binder.clearCallingIdentity();
- try {
- for (int i = 0; i < packageNames.length; i++) {
- final String packageName = packageNames[i];
- if (callingPackage.equals(packageName)) {
- Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
- + (suspended ? "" : "un") + "suspend itself. Ignoring");
- unactionedPackages.add(packageName);
- continue;
- }
- PackageSetting pkgSetting;
- synchronized (mPackages) {
- 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.");
- unactionedPackages.add(packageName);
- continue;
- }
- }
- if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) {
- unactionedPackages.add(packageName);
- continue;
- }
- synchronized (mPackages) {
- pkgSetting = mSettings.mPackages.get(packageName);
- if (pkgSetting != null) {
- pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
- launcherExtras, userId);
- }
- }
- changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+
+ for (int i = 0; i < packageNames.length; i++) {
+ final String packageName = packageNames[i];
+ if (callingPackage.equals(packageName)) {
+ Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+ + (suspended ? "" : "un") + "suspend itself. Ignoring");
+ unactionedPackages.add(packageName);
+ continue;
}
- } finally {
- Binder.restoreCallingIdentity(callingId);
+ final PackageSetting pkgSetting;
+ synchronized (mPackages) {
+ 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.");
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ }
+ if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) {
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ synchronized (mPackages) {
+ pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
+ launcherExtras, userId);
+ }
+ changedPackagesList.add(packageName);
+ changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
}
+
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
@@ -13035,88 +13093,87 @@
+ " cannot query getUnsuspendablePackagesForUser for user " + userId);
}
final ArraySet<String> unactionablePackages = new ArraySet<>();
- final long identity = Binder.clearCallingIdentity();
- try {
- for (String packageName : packageNames) {
- if (!canSuspendPackageForUserInternal(packageName, userId)) {
- unactionablePackages.add(packageName);
- }
+ for (String packageName : packageNames) {
+ if (!canSuspendPackageForUserInternal(packageName, userId)) {
+ unactionablePackages.add(packageName);
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
return unactionablePackages.toArray(new String[unactionablePackages.size()]);
}
private boolean canSuspendPackageForUserInternal(String packageName, int userId) {
- if (isPackageDeviceAdmin(packageName, userId)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": has an active device admin");
- return false;
- }
-
- String activeLauncherPackageName = getActiveLauncherPackageName(userId);
- if (packageName.equals(activeLauncherPackageName)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": contains the active launcher");
- return false;
- }
-
- if (packageName.equals(mRequiredInstallerPackage)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": required for package installation");
- return false;
- }
-
- if (packageName.equals(mRequiredUninstallerPackage)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": required for package uninstallation");
- return false;
- }
-
- if (packageName.equals(mRequiredVerifierPackage)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": required for package verification");
- return false;
- }
-
- if (packageName.equals(getDefaultDialerPackageName(userId))) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": is the default dialer");
- return false;
- }
-
- if (packageName.equals(mRequiredPermissionControllerPackage)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": required for permissions management");
- return false;
- }
-
- synchronized (mPackages) {
- if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ if (isPackageDeviceAdmin(packageName, userId)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": protected package");
+ + "\": has an active device admin");
return false;
}
- // Cannot suspend static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
- Slog.w(TAG, "Cannot suspend package: " + packageName
- + " providing static shared library: "
- + pkg.staticSharedLibName);
+ String activeLauncherPackageName = getActiveLauncherPackageName(userId);
+ if (packageName.equals(activeLauncherPackageName)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": contains the active launcher");
return false;
}
- }
- if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
- Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
- return false;
- }
+ if (packageName.equals(mRequiredInstallerPackage)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": required for package installation");
+ return false;
+ }
- return true;
+ if (packageName.equals(mRequiredUninstallerPackage)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": required for package uninstallation");
+ return false;
+ }
+
+ if (packageName.equals(mRequiredVerifierPackage)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": required for package verification");
+ return false;
+ }
+
+ if (packageName.equals(getDefaultDialerPackageName(userId))) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": is the default dialer");
+ return false;
+ }
+
+ if (packageName.equals(mRequiredPermissionControllerPackage)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": required for permissions management");
+ return false;
+ }
+
+ synchronized (mPackages) {
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": protected package");
+ return false;
+ }
+
+ // Cannot suspend static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName
+ + " providing static shared library: "
+ + pkg.staticSharedLibName);
+ return false;
+ }
+ }
+
+ if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
+ Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
+ return false;
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
}
private String getActiveLauncherPackageName(int userId) {
@@ -13717,14 +13774,15 @@
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
if (bm != null) {
+ int userId = args.user.getIdentifier();
if (DEBUG_INSTALL) {
- Log.v(TAG, "token " + token + " to BM for possible restore");
+ Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
}
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
try {
- // TODO: http://b/22388012
- if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
- bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
+ if (bm.isBackupServiceActive(userId)) {
+ bm.restoreAtInstallForUser(
+ userId, res.pkg.applicationInfo.packageName, token);
} else {
doRestore = false;
}
@@ -18425,6 +18483,7 @@
true /*stopped*/,
true /*notLaunched*/,
false /*hidden*/,
+ 0 /*distractionFlags*/,
false /*suspended*/,
null /*suspendingPackage*/,
null /*dialogInfo*/,
@@ -23240,6 +23299,14 @@
}
@Override
+ public int getDistractingPackageRestrictions(String packageName, int userId) {
+ synchronized (mPackages) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE;
+ }
+ }
+
+ @Override
public int getPackageUid(String packageName, int flags, int userId) {
return PackageManagerService.this
.getPackageUid(packageName, flags, userId);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 3c22f07..58f262c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -392,6 +392,14 @@
modifyUserState(userId).hidden = hidden;
}
+ int getDistractionFlags(int userId) {
+ return readUserState(userId).distractionFlags;
+ }
+
+ void setDistractionFlags(int distractionFlags, int userId) {
+ modifyUserState(userId).distractionFlags = distractionFlags;
+ }
+
boolean getSuspended(int userId) {
return readUserState(userId).suspended;
}
@@ -423,7 +431,8 @@
}
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
- boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
+ boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended,
+ String suspendingPackage,
SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras,
PersistableBundle suspendedLauncherExtras, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
@@ -437,6 +446,7 @@
state.stopped = stopped;
state.notLaunched = notLaunched;
state.hidden = hidden;
+ state.distractionFlags = distractionFlags;
state.suspended = suspended;
state.suspendingPackage = suspendingPackage;
state.dialogInfo = dialogInfo;
@@ -607,6 +617,7 @@
}
proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType);
proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden);
+ proto.write(PackageProto.UserInfoProto.DISTRACTION_FLAGS, state.distractionFlags);
proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
if (state.suspended) {
proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.suspendingPackage);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c524dba..95da209 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -223,6 +223,7 @@
private static final String ATTR_BLOCKED = "blocked";
// New name for the above attribute.
private static final String ATTR_HIDDEN = "hidden";
+ private static final String ATTR_DISTRACTION_FLAGS = "distraction_flags";
private static final String ATTR_SUSPENDED = "suspended";
private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package";
/**
@@ -734,6 +735,7 @@
true /*stopped*/,
true /*notLaunched*/,
false /*hidden*/,
+ 0 /*distractionFlags*/,
false /*suspended*/,
null /*suspendingPackage*/,
null /*dialogInfo*/,
@@ -1628,6 +1630,7 @@
false /*stopped*/,
false /*notLaunched*/,
false /*hidden*/,
+ 0 /*distractionFlags*/,
false /*suspended*/,
null /*suspendingPackage*/,
null /*dialogInfo*/,
@@ -1703,6 +1706,8 @@
hidden = hiddenStr == null
? hidden : Boolean.parseBoolean(hiddenStr);
+ final int distractionFlags = XmlUtils.readIntAttribute(parser,
+ ATTR_DISTRACTION_FLAGS, 0);
final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED,
false);
String suspendingPackage = parser.getAttributeValue(null,
@@ -1781,7 +1786,8 @@
setBlockUninstallLPw(userId, name, true);
}
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
- hidden, suspended, suspendingPackage, suspendDialogInfo,
+ hidden, distractionFlags, suspended, suspendingPackage,
+ suspendDialogInfo,
suspendedAppExtras, suspendedLauncherExtras, instantApp, virtualPreload,
enabledCaller, enabledComponents, disabledComponents, verifState,
linkGeneration, installReason, harmfulAppWarning);
@@ -2089,6 +2095,10 @@
if (ustate.hidden) {
serializer.attribute(null, ATTR_HIDDEN, "true");
}
+ if (ustate.distractionFlags != 0) {
+ serializer.attribute(null, ATTR_DISTRACTION_FLAGS,
+ Integer.toString(ustate.distractionFlags));
+ }
if (ustate.suspended) {
serializer.attribute(null, ATTR_SUSPENDED, "true");
if (ustate.suspendingPackage != null) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index c297c62..ac05ee8 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -168,7 +168,6 @@
} else {
session.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED);
}
- mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId);
}
void commitSession(@NonNull PackageInstallerSession session) {
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index b488337..c0ec367 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -406,7 +406,6 @@
@Nullable
private ArraySet<String> getRoleHoldersInternal(@NonNull String roleName,
@UserIdInt int userId) {
- migrateRoleIfNecessary(roleName, userId);
RoleUserState userState = getOrCreateUserState(userId);
return userState.getRoleHolders(roleName);
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 8021265..d12f7ed 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -977,6 +977,8 @@
ensureRollbackDataLoadedLocked();
mAvailableRollbacks.add(data);
}
+
+ scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
} catch (IOException e) {
Log.e(TAG, "Unable to enable rollback", e);
removeFile(data.backupDir);
diff --git a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
index 438c303..d77cf90 100644
--- a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
+++ b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
@@ -23,6 +23,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.StatsLog;
import java.security.GeneralSecurityException;
import java.util.Arrays;
@@ -66,12 +67,14 @@
private final Context mContext;
private final String mSourcePackage;
+ private final SignedConfigEvent mEvent;
private final SignatureVerifier mVerifier;
- GlobalSettingsConfigApplicator(Context context, String sourcePackage) {
+ GlobalSettingsConfigApplicator(Context context, String sourcePackage, SignedConfigEvent event) {
mContext = context;
mSourcePackage = sourcePackage;
- mVerifier = new SignatureVerifier();
+ mEvent = event;
+ mVerifier = new SignatureVerifier(mEvent);
}
private boolean checkSignature(String data, String signature) {
@@ -79,6 +82,7 @@
return mVerifier.verifySignature(data, signature);
} catch (GeneralSecurityException e) {
Slog.e(TAG, "Failed to verify signature", e);
+ mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SECURITY_EXCEPTION;
return false;
}
}
@@ -109,14 +113,17 @@
SignedConfig config;
try {
config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS);
+ mEvent.version = config.version;
} catch (InvalidConfigException e) {
Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e);
+ mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__INVALID_CONFIG;
return;
}
int currentVersion = getCurrentConfigVersion();
if (currentVersion >= config.version) {
Slog.i(TAG, "Global settings from package " + mSourcePackage
+ " is older than existing: " + config.version + "<=" + currentVersion);
+ mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__OLD_CONFIG;
return;
}
// We have new config!
@@ -126,10 +133,12 @@
config.getMatchingConfig(Build.VERSION.SDK_INT);
if (matchedConfig == null) {
Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring");
+ mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__NOT_APPLICABLE;
return;
}
Slog.i(TAG, "Updating global settings to version " + config.version);
updateCurrentConfig(config.version, matchedConfig.values);
+ mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__APPLIED;
}
}
diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
index 944db84..fcf40cf 100644
--- a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
+++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
@@ -18,6 +18,7 @@
import android.os.Build;
import android.util.Slog;
+import android.util.StatsLog;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
@@ -42,11 +43,18 @@
private static final String DEBUG_KEY =
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60"
+ "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==";
+ private static final String PROD_KEY =
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+lky6wKyGL6lE1VrD0YTMHwb0Xwc+tzC8MvnrzVxodvTp"
+ + "VY/jV7V+Zktcx+pry43XPABFRXtbhTo+qykhyBA1g==";
+ private final SignedConfigEvent mEvent;
private final PublicKey mDebugKey;
+ private final PublicKey mProdKey;
- public SignatureVerifier() {
- mDebugKey = createKey(DEBUG_KEY);
+ public SignatureVerifier(SignedConfigEvent event) {
+ mEvent = event;
+ mDebugKey = Build.IS_DEBUGGABLE ? createKey(DEBUG_KEY) : null;
+ mProdKey = createKey(PROD_KEY);
}
private static PublicKey createKey(String base64) {
@@ -67,6 +75,14 @@
}
}
+ private boolean verifyWithPublicKey(PublicKey key, byte[] data, byte[] signature)
+ throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ Signature verifier = Signature.getInstance("SHA256withECDSA");
+ verifier.initVerify(key);
+ verifier.update(data);
+ return verifier.verify(signature);
+ }
+
/**
* Verify a signature for signed config.
*
@@ -80,6 +96,7 @@
try {
signature = Base64.getDecoder().decode(base64Signature);
} catch (IllegalArgumentException e) {
+ mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_SIGNATURE;
Slog.e(TAG, "Failed to base64 decode signature");
return false;
}
@@ -89,11 +106,9 @@
if (Build.IS_DEBUGGABLE) {
if (mDebugKey != null) {
if (DBG) Slog.w(TAG, "Trying to verify signature using debug key");
- Signature verifier = Signature.getInstance("SHA256withECDSA");
- verifier.initVerify(mDebugKey);
- verifier.update(data);
- if (verifier.verify(signature)) {
+ if (verifyWithPublicKey(mDebugKey, data, signature)) {
Slog.i(TAG, "Verified config using debug key");
+ mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__DEBUG;
return true;
} else {
if (DBG) Slog.i(TAG, "Config verification failed using debug key");
@@ -102,8 +117,18 @@
Slog.w(TAG, "Debuggable build, but have no debug key");
}
}
- // TODO verify production key.
- Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION");
- return false;
+ if (mProdKey == null) {
+ Slog.e(TAG, "No prod key; construction failed?");
+ return false;
+ }
+ if (verifyWithPublicKey(mProdKey, data, signature)) {
+ Slog.i(TAG, "Verified config using production key");
+ mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__PRODUCTION;
+ return true;
+ } else {
+ if (DBG) Slog.i(TAG, "Verification failed using production key");
+ mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED;
+ return false;
+ }
}
}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java
new file mode 100644
index 0000000..2f2062c
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.signedconfig;
+
+import android.util.StatsLog;
+
+/**
+ * Helper class to allow a SignedConfigReported event to be built up in stages.
+ */
+public class SignedConfigEvent {
+
+ public int type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__UNKNOWN_TYPE;
+ public int status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__UNKNOWN_STATUS;
+ public int version = 0;
+ public String fromPackage = null;
+ public int verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__NO_KEY;
+
+ /**
+ * Write this event to statslog.
+ */
+ public void send() {
+ StatsLog.write(StatsLog.SIGNED_CONFIG_REPORTED,
+ type, status, version, fromPackage, verifiedWith);
+ }
+
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
index 6bcee14..dc39542 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -26,6 +26,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.util.Slog;
+import android.util.StatsLog;
import com.android.server.LocalServices;
@@ -82,21 +83,30 @@
}
if (metaData.containsKey(KEY_GLOBAL_SETTINGS)
&& metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) {
- String config = metaData.getString(KEY_GLOBAL_SETTINGS);
- String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE);
+ SignedConfigEvent event = new SignedConfigEvent();
try {
- // Base64 encoding is standard (not URL safe) encoding: RFC4648
- config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
- } catch (IllegalArgumentException iae) {
- Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName);
- return;
+ event.type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__GLOBAL_SETTINGS;
+ event.fromPackage = packageName;
+ String config = metaData.getString(KEY_GLOBAL_SETTINGS);
+ String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE);
+ try {
+ // Base64 encoding is standard (not URL safe) encoding: RFC4648
+ config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
+ } catch (IllegalArgumentException iae) {
+ Slog.e(TAG, "Failed to base64 decode global settings config from "
+ + packageName);
+ event.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_CONFIG;
+ return;
+ }
+ if (DBG) {
+ Slog.d(TAG, "Got global settings config: " + config);
+ Slog.d(TAG, "Got global settings signature: " + signature);
+ }
+ new GlobalSettingsConfigApplicator(mContext, packageName, event).applyConfig(
+ config, signature);
+ } finally {
+ event.send();
}
- if (DBG) {
- Slog.d(TAG, "Got global settings config: " + config);
- Slog.d(TAG, "Got global settings signature: " + signature);
- }
- new GlobalSettingsConfigApplicator(mContext, packageName).applyConfig(
- config, signature);
} else {
if (DBG) Slog.d(TAG, "Package has no global settings config/signature.");
}
diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java
index 315d5e3..1d1c28f 100644
--- a/services/core/java/com/android/server/slice/SlicePermissionManager.java
+++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java
@@ -175,18 +175,24 @@
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) {
+ Persistable p = null;
+ while (parser.parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if (parser.parser.getEventType() == XmlPullParser.START_TAG) {
+ if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) {
+ p = SliceClientPermissions.createFrom(parser.parser, tracker);
+ } else {
+ p = SliceProviderPermissions.createFrom(parser.parser, tracker);
+ }
+ break;
+ }
parser.parser.next();
}
- if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) {
- p = SliceClientPermissions.createFrom(parser.parser, tracker);
+ if (p != null) {
+ p.writeTo(out);
} else {
- p = SliceProviderPermissions.createFrom(parser.parser, tracker);
+ Slog.w(TAG, "Invalid or empty slice permissions file: " + file);
}
- p.writeTo(out);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b100ecd..ef3c8d5 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.ActivityManager.START_ABORTED;
import static android.app.ActivityManager.START_CANCELED;
@@ -50,6 +51,7 @@
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -745,8 +747,9 @@
// not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking
// on START_ABORTED
if (!abort) {
- abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid,
- callerApp, originatingPendingIntent, allowBackgroundActivityStart);
+ abort |= shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage,
+ realCallingUid, callerApp, originatingPendingIntent,
+ allowBackgroundActivityStart);
}
// Merge the two options bundles, while realCallerOptions takes precedence.
@@ -893,8 +896,8 @@
true /* doResume */, checkedOptions, inTask, outActivity);
}
- private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage,
- int realCallingUid, WindowProcessController callerApp,
+ private boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
+ final String callingPackage, int realCallingUid, WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
if (mService.isBackgroundActivityStartsEnabled()) {
return false;
@@ -928,6 +931,11 @@
if (callerApp != null && callerApp.areBackgroundActivityStartsAllowed()) {
return false;
}
+ // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
+ if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
+ == PERMISSION_GRANTED) {
+ return false;
+ }
// don't abort if the caller has the same uid as the recents component
if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
return false;
@@ -945,7 +953,7 @@
/** Returns true if uid has a visible window or its process is in a top state. */
private boolean isUidForeground(int uid) {
return (mService.getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP)
- || mService.mWindowManager.isAnyWindowVisibleForUid(uid);
+ || mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
}
/** Returns true if uid is in a persistent state. */
@@ -968,18 +976,19 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "logActivityStart");
final int callingUidProcState = mService.getUidStateLocked(callingUid);
final boolean callingUidHasAnyVisibleWindow =
- mService.mWindowManager.isAnyWindowVisibleForUid(callingUid);
+ mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
final int realCallingUidProcState = (callingUid == realCallingUid)
? callingUidProcState
: mService.getUidStateLocked(realCallingUid);
final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
? callingUidHasAnyVisibleWindow
- : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
+ : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(
+ realCallingUid);
final String targetPackage = (r != null) ? r.packageName : null;
final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1;
final int targetUidProcState = mService.getUidStateLocked(targetUid);
final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
- ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
+ ? mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(targetUid)
: false;
final String targetWhitelistTag = (targetUid != -1)
? mService.getPendingTempWhitelistTagForUidLocked(targetUid)
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4e70bbc..8fb7947 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -23,6 +23,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -280,6 +281,15 @@
}
/**
+ * Returns true if the callingUid has any non-toast window currently visible to the user.
+ */
+ boolean isAnyNonToastWindowVisibleForUid(int callingUid) {
+ return forAllWindows(w -> {
+ return w.getOwningUid() == callingUid && w.isVisible() && w.mAttrs.type != TYPE_TOAST;
+ }, true /* traverseTopToBottom */);
+ }
+
+ /**
* Returns the app window token for the input binder if it exist in the system.
* NOTE: Only one AppWindowToken is allowed to exist in the system for a binder token, since
* AppWindowToken represents an activity which can only exist on one display.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9b82f5c..90506e7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5716,17 +5716,6 @@
}
/**
- * Returns true if the callingUid has any window currently visible to the user.
- */
- public boolean isAnyWindowVisibleForUid(int callingUid) {
- synchronized (mGlobalLock) {
- return mRoot.forAllWindows(w -> {
- return w.getOwningUid() == callingUid && w.isVisible();
- }, true /* traverseTopToBottom */);
- }
- }
-
- /**
* Called when a task has been removed from the recent tasks list.
* <p>
* Note: This doesn't go through {@link TaskWindowContainerController} yet as the window
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index 769a9d4..b8db3f3 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -46,6 +46,7 @@
import android.util.SparseArray;
import com.android.server.backup.testing.TransportData;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowBinder;
import org.junit.After;
@@ -65,7 +66,7 @@
/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBinder.class})
+@Config(shadows = {ShadowApplicationPackageManager.class, ShadowBinder.class})
@Presubmit
public class BackupManagerServiceTest {
private static final String TEST_PACKAGE = "package";
@@ -1075,7 +1076,7 @@
createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
FullBackupJob job = new FullBackupJob();
- backupManagerService.beginFullBackup(job);
+ backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
verify(mUserOneService).beginFullBackup(job);
}
@@ -1086,7 +1087,7 @@
BackupManagerService backupManagerService = createService();
FullBackupJob job = new FullBackupJob();
- backupManagerService.beginFullBackup(job);
+ backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
verify(mUserOneService, never()).beginFullBackup(job);
}
@@ -1097,7 +1098,7 @@
BackupManagerService backupManagerService =
createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
- backupManagerService.endFullBackup();
+ backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
verify(mUserOneService).endFullBackup();
}
@@ -1107,7 +1108,7 @@
public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
BackupManagerService backupManagerService = createService();
- backupManagerService.endFullBackup();
+ backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
verify(mUserOneService, never()).endFullBackup();
}
diff --git a/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java
new file mode 100644
index 0000000..9a78d0b3
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.UserIdInt;
+import android.app.job.JobScheduler;
+import android.content.Context;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowJobScheduler;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowJobScheduler.class})
+@Presubmit
+public class FullBackupJobTest {
+ private Context mContext;
+ private BackupManagerConstants mConstants;
+ private ShadowJobScheduler mShadowJobScheduler;
+
+ @UserIdInt private int mUserOneId;
+ @UserIdInt private int mUserTwoId;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = RuntimeEnvironment.application;
+ mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
+ mConstants.start();
+
+ mShadowJobScheduler = Shadows.shadowOf(mContext.getSystemService(JobScheduler.class));
+
+ mUserOneId = UserHandle.USER_SYSTEM;
+ mUserTwoId = mUserOneId + 1;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mConstants.stop();
+ FullBackupJob.cancel(mUserOneId, mContext);
+ FullBackupJob.cancel(mUserTwoId, mContext);
+ }
+
+ @Test
+ public void testSchedule_afterScheduling_jobExists() {
+ FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+ FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull();
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull();
+ }
+
+ @Test
+ public void testCancel_afterCancelling_jobDoesntExist() {
+ FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+ FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+ FullBackupJob.cancel(mUserOneId, mContext);
+ FullBackupJob.cancel(mUserTwoId, mContext);
+
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull();
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull();
+ }
+//
+ @Test
+ public void testSchedule_onlySchedulesForRequestedUser() {
+ FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull();
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull();
+ }
+//
+ @Test
+ public void testCancel_onlyCancelsForRequestedUser() {
+ FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+ FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+ FullBackupJob.cancel(mUserOneId, mContext);
+
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull();
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull();
+ }
+
+ private static int getJobIdForUserId(int userId) {
+ return JobIdManager.getJobIdForUserId(FullBackupJob.MIN_JOB_ID, FullBackupJob.MAX_JOB_ID,
+ userId);
+ }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java b/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java
new file mode 100644
index 0000000..f8bb1ee
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.UserIdInt;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import static org.testng.Assert.expectThrows;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class JobIdManagerTest {
+ private static final int MIN_JOB_ID = 10;
+ private static final int MAX_JOB_ID = 20;
+
+ @UserIdInt private int mUserOneId;
+ @UserIdInt private int mUserTwoId;
+
+ @Before
+ public void setUp() {
+ mUserOneId = UserHandle.USER_SYSTEM;
+ mUserTwoId = mUserOneId + 1;
+ }
+
+ @Test
+ public void testGetJobIdForUserId_returnsDifferentJobIdsForDifferentUsers() {
+ int jobIdOne = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+ int jobIdTwo = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserTwoId);
+
+ assertThat(jobIdOne).isNotEqualTo(jobIdTwo);
+ }
+
+ @Test
+ public void testGetJobIdForUserId_returnsSameJobIdForSameUser() {
+ int jobIdOne = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+ int jobIdTwo = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+
+ assertThat(jobIdOne).isEqualTo(jobIdTwo);
+ }
+
+ @Test
+ public void testGetJobIdForUserId_throwsExceptionIfRangeIsExceeded() {
+ expectThrows(
+ RuntimeException.class,
+ () -> JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID,
+ MAX_JOB_ID + 1));
+ }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
index 8e17209..8d9e44f 100644
--- a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
@@ -18,8 +18,10 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.os.Handler;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import org.junit.After;
@@ -35,32 +37,67 @@
private Context mContext;
private BackupManagerConstants mConstants;
+ @UserIdInt private int mUserOneId;
+ @UserIdInt private int mUserTwoId;
+
@Before
public void setUp() throws Exception {
mContext = RuntimeEnvironment.application;
mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
mConstants.start();
+
+ mUserOneId = UserHandle.USER_SYSTEM;
+ mUserTwoId = mUserOneId + 1;
}
@After
public void tearDown() throws Exception {
mConstants.stop();
- KeyValueBackupJob.cancel(mContext);
+ KeyValueBackupJob.cancel(mUserOneId, mContext);
+ KeyValueBackupJob.cancel(mUserTwoId, mContext);
}
@Test
public void testIsScheduled_beforeScheduling_returnsFalse() {
- boolean isScheduled = KeyValueBackupJob.isScheduled();
-
- assertThat(isScheduled).isFalse();
+ assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+ assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
}
@Test
public void testIsScheduled_afterScheduling_returnsTrue() {
- KeyValueBackupJob.schedule(mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
- boolean isScheduled = KeyValueBackupJob.isScheduled();
+ assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue();
+ assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue();
+ }
- assertThat(isScheduled).isTrue();
+ @Test
+ public void testIsScheduled_afterCancelling_returnsFalse() {
+ KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+ KeyValueBackupJob.cancel(mUserOneId, mContext);
+ KeyValueBackupJob.cancel(mUserTwoId, mContext);
+
+ assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+ assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
+ }
+
+ @Test
+ public void testIsScheduled_afterScheduling_returnsTrueOnlyForScheduledUser() {
+ KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+
+ assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue();
+ assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
+ }
+
+ @Test
+ public void testIsScheduled_afterCancelling_returnsFalseOnlyForCancelledUser() {
+ KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+ KeyValueBackupJob.cancel(mUserOneId, mContext);
+
+ assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+ assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue();
}
}
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 8d5c301..3b7fa3d 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -42,6 +42,8 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.os.Binder;
import android.os.HandlerThread;
import android.os.PowerManager;
@@ -54,6 +56,7 @@
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.testing.shadows.ShadowAppBackupUtils;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowBinder;
import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
@@ -80,7 +83,7 @@
* UserBackupManagerService} that performs operations for its target user.
*/
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowAppBackupUtils.class})
+@Config(shadows = {ShadowAppBackupUtils.class, ShadowApplicationPackageManager.class})
@Presubmit
public class UserBackupManagerServiceTest {
private static final String TAG = "BMSTest";
@@ -137,6 +140,7 @@
public void tearDown() throws Exception {
mBackupThread.quit();
ShadowAppBackupUtils.reset();
+ ShadowApplicationPackageManager.reset();
}
/**
@@ -195,6 +199,7 @@
public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
setUpCurrentTransport(mTransportManager, mTransport);
+ registerPackages(PACKAGE_1);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
@@ -210,6 +215,7 @@
public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
+ registerPackages(PACKAGE_1);
ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
@@ -228,6 +234,7 @@
public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
setUpCurrentTransport(mTransportManager, mTransport);
+ registerPackages(PACKAGE_1);
ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
@@ -245,6 +252,7 @@
public void testFilterAppsEligibleForBackup() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
+ registerPackages(PACKAGE_1, PACKAGE_2);
ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
@@ -264,6 +272,7 @@
@Test
public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ registerPackages(PACKAGE_1, PACKAGE_2);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
String[] filtered =
@@ -281,6 +290,7 @@
public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
setUpCurrentTransport(mTransportManager, mTransport);
+ registerPackages(PACKAGE_1, PACKAGE_2);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
@@ -749,7 +759,7 @@
private void setUpForRequestBackup(String... packages) throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
for (String packageName : packages) {
- mShadowPackageManager.addPackage(packageName);
+ registerPackages(packageName);
ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
}
setUpCurrentTransport(mTransportManager, mTransport);
@@ -864,7 +874,7 @@
@Test
public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- mShadowPackageManager.addPackage(PACKAGE_1);
+ registerPackages(PACKAGE_1);
setUpCurrentTransport(mTransportManager, mTransport);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.setEnabled(true);
@@ -1127,6 +1137,22 @@
backupManagerService.setPowerManager(powerManagerMock);
}
+ private PackageInfo getPackageInfo(String packageName) {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = packageName;
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.packageName = packageName;
+ return packageInfo;
+ }
+
+ private void registerPackages(String... packages) {
+ for (String packageName : packages) {
+ PackageInfo packageInfo = getPackageInfo(packageName);
+ mShadowPackageManager.installPackage(packageInfo);
+ ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
+ }
+ }
+
/**
* We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
* extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
@@ -1137,8 +1163,9 @@
* Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
* BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
*/
- public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
- ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
+ public static void schedule(int userId, Context ctx, long delay,
+ BackupManagerConstants constants) {
+ ShadowKeyValueBackupJob.schedule(userId, ctx, delay, constants);
throw new IllegalArgumentException();
}
}
diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
index b754356..bfb2b14 100644
--- a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
@@ -29,6 +29,7 @@
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.testing.BackupManagerServiceTestUtils;
import com.android.server.backup.testing.TestUtils;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import org.junit.Before;
import org.junit.Test;
@@ -37,6 +38,7 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import java.io.File;
@@ -45,6 +47,7 @@
* UserBackupManagerService}.
*/
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowApplicationPackageManager.class})
@Presubmit
public class SetupObserverTest {
private static final String TAG = "SetupObserverTest";
@@ -125,7 +128,7 @@
setupObserver.onChange(true);
- assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+ assertThat(KeyValueBackupJob.isScheduled(mUserBackupManagerService.getUserId())).isTrue();
// Verifies that the full backup job is scheduled. The job is scheduled via a posted message
// on the backup handler so we verify that a message exists.
assertThat(mUserBackupManagerService.getBackupHandler().hasMessagesOrCallbacks()).isTrue();
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 7dac795..4811523 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -71,7 +71,6 @@
import android.annotation.Nullable;
import android.app.Application;
-import android.app.ApplicationPackageManager;
import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
@@ -116,6 +115,7 @@
import com.android.server.backup.testing.TransportTestUtils;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.testing.shadows.FrameworkShadowLooper;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowBackupDataInput;
import com.android.server.testing.shadows.ShadowBackupDataOutput;
import com.android.server.testing.shadows.ShadowEventLog;
@@ -135,8 +135,6 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.shadows.ShadowQueuedWork;
@@ -160,7 +158,7 @@
@Config(
shadows = {
FrameworkShadowLooper.class,
- KeyValueBackupTaskTest.ShadowApplicationPackageManager.class,
+ ShadowApplicationPackageManager.class,
ShadowBackupDataInput.class,
ShadowBackupDataOutput.class,
ShadowEventLog.class,
@@ -2437,11 +2435,12 @@
private AgentMock setUpAgent(PackageData packageData) {
try {
+ String packageName = packageData.packageName;
mPackageManager.setApplicationEnabledSetting(
- packageData.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+ packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
PackageInfo packageInfo = getPackageInfo(packageData);
mShadowPackageManager.installPackage(packageInfo);
- ShadowApplicationPackageManager.setPackageInfo(packageInfo);
+ ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
mContext.sendBroadcast(getPackageAddedIntent(packageData));
// Run the backup looper because on the receiver we post MSG_SCHEDULE_BACKUP_PACKAGE
mShadowBackupLooper.runToEndOfTasks();
@@ -2537,7 +2536,7 @@
private PackageManagerBackupAgent createPmAgent() {
PackageManagerBackupAgent pmAgent =
- new PackageManagerBackupAgent(mApplication.getPackageManager());
+ new PackageManagerBackupAgent(mApplication.getPackageManager(), USER_ID);
pmAgent.attach(mApplication);
pmAgent.onCreate();
return pmAgent;
@@ -2727,7 +2726,7 @@
throws RemoteException, IOException {
verify(transportMock.transport).requestBackupTime();
assertBackupPendingFor(packages);
- assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+ assertThat(KeyValueBackupJob.isScheduled(mBackupManagerService.getUserId())).isTrue();
}
private void assertBackupPendingFor(PackageData... packages) throws IOException {
@@ -2842,7 +2841,7 @@
ThrowingPackageManagerBackupAgent(
PackageManager packageManager, RuntimeException exception) {
- super(packageManager);
+ super(packageManager, USER_ID);
mException = exception;
}
@@ -2854,29 +2853,4 @@
throw mException;
}
}
-
- /**
- * Extends {@link org.robolectric.shadows.ShadowApplicationPackageManager} to return the correct
- * package in user-specific invocations.
- */
- @Implements(value = ApplicationPackageManager.class)
- public static class ShadowApplicationPackageManager
- extends org.robolectric.shadows.ShadowApplicationPackageManager {
- private static PackageInfo sPackageInfo;
-
- static void setPackageInfo(PackageInfo packageInfo) {
- sPackageInfo = packageInfo;
- }
-
- @Override
- protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) {
- return sPackageInfo;
- }
-
- /** Clear {@link #sPackageInfo}. */
- @Resetter
- public static void reset() {
- sPackageInfo = null;
- }
- }
}
diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 4009876..f17a9fe 100644
--- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -57,6 +57,7 @@
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowEventLog;
import com.android.server.testing.shadows.ShadowPerformUnifiedRestoreTask;
@@ -77,7 +78,13 @@
import java.util.ArrayDeque;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class, ShadowBinder.class})
+@Config(
+ shadows = {
+ ShadowApplicationPackageManager.class,
+ ShadowBinder.class,
+ ShadowEventLog.class,
+ ShadowPerformUnifiedRestoreTask.class
+ })
@Presubmit
public class ActiveRestoreSessionTest {
private static final String PACKAGE_1 = "com.example.package1";
@@ -140,6 +147,7 @@
@After
public void tearDown() throws Exception {
+ ShadowApplicationPackageManager.reset();
ShadowPerformUnifiedRestoreTask.reset();
}
@@ -561,7 +569,8 @@
packageInfo.packageName = packageName;
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.uid = uid;
- mShadowPackageManager.addPackage(packageInfo);
+ mShadowPackageManager.installPackage(packageInfo);
+ ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
}
private IRestoreSession createActiveRestoreSession(
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
index 5fffb14..aefc871 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
@@ -54,7 +54,10 @@
@Implementation
protected static boolean appIsRunningAndEligibleForBackupWithTransport(
- @Nullable TransportClient transportClient, String packageName, PackageManager pm) {
+ @Nullable TransportClient transportClient,
+ String packageName,
+ PackageManager pm,
+ int userId) {
return sAppsRunningAndEligibleForBackupWithTransport.contains(packageName);
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
new file mode 100644
index 0000000..dc32209
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.testing.shadows;
+
+import static android.content.pm.PackageManager.NameNotFoundException;
+
+import android.app.ApplicationPackageManager;
+import android.content.pm.PackageInfo;
+import android.util.ArrayMap;
+
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Extends {@link org.robolectric.shadows.ShadowApplicationPackageManager} to return the correct
+ * package in user-specific invocations.
+ */
+@Implements(value = ApplicationPackageManager.class)
+public class ShadowApplicationPackageManager
+ extends org.robolectric.shadows.ShadowApplicationPackageManager {
+ private static final Map<String, PackageInfo> sPackageInfos = new ArrayMap<>();
+ private static final List<PackageInfo> sInstalledPackages = new ArrayList<>();
+
+ /**
+ * Registers the package {@code packageName} to be returned when invoking {@link
+ * ApplicationPackageManager#getPackageInfoAsUser(String, int, int)} and {@link
+ * ApplicationPackageManager#getInstalledPackagesAsUser(int, int)}.
+ */
+ public static void addInstalledPackage(String packageName, PackageInfo packageInfo) {
+ sPackageInfos.put(packageName, packageInfo);
+ sInstalledPackages.add(packageInfo);
+ }
+
+ @Override
+ protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+ throws NameNotFoundException {
+ if (!sPackageInfos.containsKey(packageName)) {
+ throw new NameNotFoundException(packageName);
+ }
+ return sPackageInfos.get(packageName);
+ }
+
+ @Override
+ protected List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
+ return sInstalledPackages;
+ }
+
+ /** Clear package state. */
+ @Resetter
+ public static void reset() {
+ sPackageInfos.clear();
+ sInstalledPackages.clear();
+ org.robolectric.shadows.ShadowApplicationPackageManager.reset();
+ }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
index 23c44b0e..f90ea6a 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
@@ -34,7 +34,8 @@
}
@Implementation
- protected static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+ protected static void schedule(int userId, Context ctx, long delay,
+ BackupManagerConstants constants) {
callingUid = Binder.getCallingUid();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
index 8109b1c..1dfce51 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -26,6 +26,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerService;
import org.junit.Before;
import org.junit.Test;
@@ -62,13 +63,15 @@
return false;
}
});
+ mService.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+ mService.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
}
@Test
@UiThreadTest
public void testCreateWorks() {
AppErrorDialog.Data data = new AppErrorDialog.Data();
- data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345);
+ data.proc = new ProcessRecord(mService, mContext.getApplicationInfo(), "name", 12345);
data.result = new AppErrorResult();
AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data);
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index db83505..6a9a121 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -1149,31 +1149,31 @@
@Test
public void beginFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.beginFullBackup(new FullBackupJob());
+ mTrampoline.beginFullBackup(mUserId, new FullBackupJob());
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
public void beginFullBackup_forwarded() throws RemoteException {
FullBackupJob fullBackupJob = new FullBackupJob();
- when(mBackupManagerServiceMock.beginFullBackup(fullBackupJob)).thenReturn(true);
+ when(mBackupManagerServiceMock.beginFullBackup(mUserId, fullBackupJob)).thenReturn(true);
mTrampoline.initializeService();
- assertTrue(mTrampoline.beginFullBackup(fullBackupJob));
- verify(mBackupManagerServiceMock).beginFullBackup(fullBackupJob);
+ assertTrue(mTrampoline.beginFullBackup(mUserId, fullBackupJob));
+ verify(mBackupManagerServiceMock).beginFullBackup(mUserId, fullBackupJob);
}
@Test
public void endFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.endFullBackup();
+ mTrampoline.endFullBackup(mUserId);
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
public void endFullBackup_forwarded() throws RemoteException {
mTrampoline.initializeService();
- mTrampoline.endFullBackup();
- verify(mBackupManagerServiceMock).endFullBackup();
+ mTrampoline.endFullBackup(mUserId);
+ verify(mBackupManagerServiceMock).endFullBackup(mUserId);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 517b5ad..6d28ed1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -35,6 +35,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.SuspendDialogInfo;
@@ -226,8 +227,8 @@
settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3));
// now read and verify
settingsUnderTest.readPackageRestrictionsLPr(0);
- final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1).
- readUserState(0);
+ final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1)
+ .readUserState(0);
assertThat(readPus1.suspended, is(true));
assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
assertThat(readPus1.dialogInfo, equalTo(dialogInfo1));
@@ -235,16 +236,16 @@
assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
is(true));
- final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2).
- readUserState(0);
+ final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2)
+ .readUserState(0);
assertThat(readPus2.suspended, is(true));
assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
assertThat(readPus2.dialogInfo, is(nullValue()));
assertThat(readPus2.suspendedAppExtras, is(nullValue()));
assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
- final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3).
- readUserState(0);
+ final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3)
+ .readUserState(0);
assertThat(readPus3.suspended, is(false));
assertThat(readPus3.suspendingPackage, is(nullValue()));
assertThat(readPus3.dialogInfo, is(nullValue()));
@@ -254,11 +255,59 @@
@Test
public void testPackageRestrictionsSuspendedDefault() {
- final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
+ final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
assertThat(defaultSetting.getSuspended(0), is(false));
}
@Test
+ public void testReadWritePackageRestrictions_distractionFlags() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, new Object());
+ final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+ final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
+ final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3);
+
+ final int distractionFlags1 = PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
+ ps1.setDistractionFlags(distractionFlags1, 0);
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
+
+ final int distractionFlags2 = PackageManager.RESTRICTION_HIDE_NOTIFICATIONS
+ | PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
+ ps2.setDistractionFlags(distractionFlags2, 0);
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
+
+ final int distractionFlags3 = PackageManager.RESTRICTION_NONE;
+ ps3.setDistractionFlags(distractionFlags3, 0);
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
+
+ settingsUnderTest.writePackageRestrictionsLPr(0);
+
+ settingsUnderTest.mPackages.clear();
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3));
+ // now read and verify
+ settingsUnderTest.readPackageRestrictionsLPr(0);
+ final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1)
+ .readUserState(0);
+ assertThat(readPus1.distractionFlags, is(distractionFlags1));
+
+ final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2)
+ .readUserState(0);
+ assertThat(readPus2.distractionFlags, is(distractionFlags2));
+
+ final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3)
+ .readUserState(0);
+ assertThat(readPus3.distractionFlags, is(distractionFlags3));
+ }
+
+ @Test
+ public void testPackageRestrictionsDistractionFlagsDefault() {
+ final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
+ assertThat(defaultSetting.getDistractionFlags(0), is(PackageManager.RESTRICTION_NONE));
+ }
+
+ @Test
public void testEnableDisable() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
@@ -692,6 +741,7 @@
assertThat(userState.notLaunched, is(notLaunched));
assertThat(userState.stopped, is(stopped));
assertThat(userState.suspended, is(false));
+ assertThat(userState.distractionFlags, is(0));
if (oldUserState != null) {
assertThat(userState.equals(oldUserState), is(not(userStateChanged)));
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index f0ed612..8eaf35f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -22,6 +22,7 @@
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
+import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
import android.content.pm.SuspendDialogInfo;
import android.os.PersistableBundle;
@@ -227,4 +228,19 @@
assertThat(testUserState1.equals(testUserState2), is(true));
}
+ @Test
+ public void testPackageUserState06() {
+ final PackageUserState userState1 = new PackageUserState();
+ assertThat(userState1.distractionFlags, is(PackageManager.RESTRICTION_NONE));
+ userState1.distractionFlags = PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
+
+ final PackageUserState copyOfUserState1 = new PackageUserState(userState1);
+ assertThat(userState1.distractionFlags, is(copyOfUserState1.distractionFlags));
+ assertThat(userState1.equals(copyOfUserState1), is(true));
+
+ final PackageUserState userState2 = new PackageUserState(userState1);
+ userState2.distractionFlags = PackageManager.RESTRICTION_HIDE_NOTIFICATIONS;
+ assertThat(userState1.equals(userState2), is(false));
+ }
+
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 5638cb36..c79e1db0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -15,8 +15,11 @@
*/
package com.android.server.notification;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static junit.framework.TestCase.assertEquals;
+
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyInt;
@@ -153,7 +156,7 @@
.build();
mRecordGroupGSortA = new NotificationRecord(mContext, new StatusBarNotification(
PKG, PKG, 1, null, 0, 0, mNotiGroupGSortA, user,
- null, System.currentTimeMillis()), getDefaultChannel());
+ null, System.currentTimeMillis()), getLowChannel());
mNotiGroupGSortB = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentTitle("B")
@@ -163,7 +166,7 @@
.build();
mRecordGroupGSortB = new NotificationRecord(mContext, new StatusBarNotification(
PKG, PKG, 1, null, 0, 0, mNotiGroupGSortB, user,
- null, System.currentTimeMillis()), getDefaultChannel());
+ null, System.currentTimeMillis()), getLowChannel());
mNotiNoGroup = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentTitle("C")
@@ -171,7 +174,7 @@
.build();
mRecordNoGroup = new NotificationRecord(mContext, new StatusBarNotification(
PKG, PKG, 1, null, 0, 0, mNotiNoGroup, user,
- null, System.currentTimeMillis()), getDefaultChannel());
+ null, System.currentTimeMillis()), getLowChannel());
mNotiNoGroup2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentTitle("D")
@@ -179,7 +182,7 @@
.build();
mRecordNoGroup2 = new NotificationRecord(mContext, new StatusBarNotification(
PKG, PKG, 1, null, 0, 0, mNotiNoGroup2, user,
- null, System.currentTimeMillis()), getDefaultChannel());
+ null, System.currentTimeMillis()), getLowChannel());
mNotiNoGroupSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentTitle("E")
@@ -188,7 +191,7 @@
.build();
mRecordNoGroupSortA = new NotificationRecord(mContext, new StatusBarNotification(
PKG, PKG, 1, null, 0, 0, mNotiNoGroupSortA, user,
- null, System.currentTimeMillis()), getDefaultChannel());
+ null, System.currentTimeMillis()), getLowChannel());
mAudioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
@@ -197,11 +200,16 @@
.build();
}
- private NotificationChannel getDefaultChannel() {
+ private NotificationChannel getLowChannel() {
return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
IMPORTANCE_LOW);
}
+ private NotificationChannel getDefaultChannel() {
+ return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+ IMPORTANCE_DEFAULT);
+ }
+
@Test
public void testSortShouldRespectCritical() throws Exception {
ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(7);
@@ -285,4 +293,40 @@
ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
mHelper.sort(notificationList);
}
+
+ @Test
+ public void testGroupNotifications_highestIsProxy() {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<>();
+ // this should be the last in the list, except it's in a group with a high child
+ Notification lowSummaryN = new Notification.Builder(mContext, "")
+ .setGroup("group")
+ .setGroupSummary(true)
+ .build();
+ NotificationRecord lowSummary = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 1, "summary", 0, 0, lowSummaryN, USER,
+ null, System.currentTimeMillis()), getLowChannel());
+ notificationList.add(lowSummary);
+
+ Notification lowN = new Notification.Builder(mContext, "").build();
+ NotificationRecord low = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 1, "low", 0, 0, lowN, USER,
+ null, System.currentTimeMillis()), getLowChannel());
+ low.setContactAffinity(0.5f);
+ notificationList.add(low);
+
+ Notification highChildN = new Notification.Builder(mContext, "")
+ .setGroup("group")
+ .setGroupSummary(false)
+ .build();
+ NotificationRecord highChild = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 1, "child", 0, 0, highChildN, USER,
+ null, System.currentTimeMillis()), getDefaultChannel());
+ notificationList.add(highChild);
+
+ mHelper.sort(notificationList);
+
+ assertEquals(lowSummary, notificationList.get(0));
+ assertEquals(highChild, notificationList.get(1));
+ assertEquals(low, notificationList.get(2));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
index b315e51..efefee1 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
@@ -16,6 +16,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -26,6 +27,7 @@
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;
@@ -46,10 +48,12 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class SlicePermissionManagerTest extends UiServiceTestCase {
+ private static final String TAG = "SlicePerManTest";
@Test
public void testGrant() {
- File sliceDir = new File(mContext.getDataDir(), "system/slices");
+ File sliceDir = new File(mContext.getCacheDir(), "testGrantSlices");
+ Log.v(TAG, "testGrant: slice permissions stored in " + sliceDir.getAbsolutePath());
SlicePermissionManager permissions = new SlicePermissionManager(mContext,
TestableLooper.get(this).getLooper(), sliceDir);
Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT)
@@ -59,11 +63,15 @@
permissions.grantSliceAccess("my.pkg", 0, "provider.pkg", 0, uri);
assertTrue(permissions.hasPermission("my.pkg", 0, uri));
+
+ // Cleanup.
+ assertTrue(FileUtils.deleteContentsAndDir(sliceDir));
}
@Test
public void testBackup() throws XmlPullParserException, IOException {
- File sliceDir = new File(mContext.getDataDir(), "system/slices");
+ File sliceDir = new File(mContext.getCacheDir(), "testBackupSlices");
+ Log.v(TAG, "testBackup: slice permissions stored in " + sliceDir.getAbsolutePath());
Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority("authority")
.path("something").build();
@@ -90,7 +98,10 @@
TestableLooper.get(this).getLooper());
permissions.readRestore(parser);
- assertTrue(permissions.hasFullAccess("com.android.mypkg", 10));
+ if (!permissions.hasFullAccess("com.android.mypkg", 10)) {
+ fail("com.android.mypkg@10 did not have full access. backup file: "
+ + output.toString());
+ }
assertTrue(permissions.hasPermission("com.android.otherpkg", 0,
ContentProvider.maybeAddUserId(uri, 1)));
permissions.removePkg("com.android.lastpkg", 1);
@@ -102,8 +113,9 @@
}
@Test
- public void testInvalid() throws Exception {
- File sliceDir = new File(mContext.getCacheDir(), "slices-test");
+ public void testInvalid() {
+ File sliceDir = new File(mContext.getCacheDir(), "testInvalidSlices");
+ Log.v(TAG, "testInvalid: slice permissions stored in " + sliceDir.getAbsolutePath());
if (!sliceDir.exists()) {
sliceDir.mkdir();
}
@@ -118,7 +130,8 @@
@Override
public void writeTo(XmlSerializer out) throws IOException {
- throw new RuntimeException("this doesn't work");
+ throw new RuntimeException("this RuntimeException inside junk.writeTo() "
+ + "should be caught and suppressed by surrounding code");
}
};
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 7a6b2b5..ec88718 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -643,10 +643,10 @@
boolean hasForegroundActivities, boolean callerIsRecents,
boolean callerIsTempWhitelisted) {
// window visibility
- doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
- callingUid);
- doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager)
- .isAnyWindowVisibleForUid(realCallingUid);
+ doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager.mRoot)
+ .isAnyNonToastWindowVisibleForUid(callingUid);
+ doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager.mRoot)
+ .isAnyNonToastWindowVisibleForUid(realCallingUid);
// process importance
doReturn(callingUidProcState).when(mService).getUidStateLocked(callingUid);
doReturn(realCallingUidProcState).when(mService).getUidStateLocked(realCallingUid);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
new file mode 100644
index 0000000..45fe5d2
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for RootWindowContainer.
+ *
+ * Build/Install/Run:
+ * atest WmTests:RootWindowContainerTests
+ */
+@SmallTest
+@Presubmit
+public class RootWindowContainerTests extends WindowTestsBase {
+
+ private static final int FAKE_CALLING_UID = 667;
+
+ @Test
+ public void testIsAnyNonToastWindowVisibleForUid_oneToastOneNonToastBothVisible() {
+ final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
+ toastyToast.mHasSurface = true;
+ app.mHasSurface = true;
+
+ assertTrue(toastyToast.isVisible());
+ assertTrue(app.isVisible());
+ assertTrue(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+ }
+
+ @Test
+ public void testIsAnyNonToastWindowVisibleForUid_onlyToastVisible() {
+ final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
+ toastyToast.mHasSurface = true;
+
+ assertTrue(toastyToast.isVisible());
+ assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+ }
+
+ @Test
+ public void testIsAnyNonToastWindowVisibleForUid_aFewNonToastButNoneVisible() {
+ final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar", FAKE_CALLING_UID);
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
+
+ assertFalse(topBar.isVisible());
+ assertFalse(app.isVisible());
+ assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+ }
+}
+
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 638cb03..cdc0a47 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -257,6 +257,14 @@
}
}
+ WindowState createWindow(WindowState parent, int type, String name, int ownerId) {
+ synchronized (mWm.mGlobalLock) {
+ return (parent == null)
+ ? createWindow(parent, type, mDisplayContent, name, ownerId)
+ : createWindow(parent, type, parent.mToken, name, ownerId);
+ }
+ }
+
WindowState createWindowOnStack(WindowState parent, int windowingMode, int activityType,
int type, DisplayContent dc, String name) {
synchronized (mWm.mGlobalLock) {
@@ -277,7 +285,16 @@
synchronized (mWm.mGlobalLock) {
final WindowToken token = createWindowToken(
dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
- return createWindow(parent, type, token, name);
+ return createWindow(parent, type, token, name, 0 /* ownerId */);
+ }
+ }
+
+ WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
+ int ownerId) {
+ synchronized (mWm.mGlobalLock) {
+ final WindowToken token = createWindowToken(
+ dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
+ return createWindow(parent, type, token, name, ownerId);
}
}
@@ -299,6 +316,14 @@
}
WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
+ int ownerId) {
+ synchronized (mWm.mGlobalLock) {
+ return createWindow(parent, type, token, name, ownerId,
+ false /* ownerCanAddInternalSystemWindow */);
+ }
+ }
+
+ WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
int ownerId, boolean ownerCanAddInternalSystemWindow) {
return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
mWm, mMockSession, mIWindow);
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 2fc3a0d..7dc83c3 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -46,7 +46,7 @@
},
}
-cc_library_host_static {
+cc_library_static {
name: "libviewcompiler",
defaults: ["viewcompiler_defaults"],
srcs: [
@@ -58,9 +58,10 @@
"util.cc",
"layout_validation.cc",
],
+ host_supported: true,
}
-cc_binary_host {
+cc_binary {
name: "viewcompiler",
defaults: ["viewcompiler_defaults"],
srcs: [
@@ -70,6 +71,7 @@
"libgflags",
"libviewcompiler",
],
+ host_supported: true
}
cc_test_host {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 8425603..05d5a13 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -505,6 +505,14 @@
"android.telecom.extra.ORIGINAL_CONNECTION_ID";
/**
+ * Boolean connection extra key set on the extras passed to
+ * {@link Connection#sendConnectionEvent} which indicates that audio is present
+ * on the RTT call when the extra value is true.
+ */
+ public static final String EXTRA_IS_RTT_AUDIO_PRESENT =
+ "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
+
+ /**
* Connection event used to inform Telecom that it should play the on hold tone. This is used
* to play a tone when the peer puts the current call on hold. Sent to Telecom via
* {@link #sendConnectionEvent(String, Bundle)}.
@@ -619,6 +627,13 @@
*/
public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
+ /**
+ * Connection event used to inform an {@link InCallService} that the RTT audio indication
+ * has changed.
+ */
+ public static final String EVENT_RTT_AUDIO_INDICATION_CHANGED =
+ "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
+
// Flag controlling whether PII is emitted into the logs
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
diff --git a/telephony/java/android/telephony/CallAttributes.aidl b/telephony/java/android/telephony/CallAttributes.aidl
new file mode 100644
index 0000000..69127df
--- /dev/null
+++ b/telephony/java/android/telephony/CallAttributes.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable CallAttributes;
+
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
new file mode 100644
index 0000000..2b99ce1
--- /dev/null
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.TelephonyManager.NetworkType;
+
+import java.util.Objects;
+
+/**
+ * Contains information about a call's attributes as passed up from the HAL. If there are multiple
+ * ongoing calls, the CallAttributes will pertain to the call in the foreground.
+ * @hide
+ */
+@SystemApi
+public class CallAttributes implements Parcelable {
+ private PreciseCallState mPreciseCallState;
+ @NetworkType
+ private int mNetworkType; // TelephonyManager.NETWORK_TYPE_* ints
+ private CallQuality mCallQuality;
+
+
+ public CallAttributes(PreciseCallState state, @NetworkType int networkType,
+ CallQuality callQuality) {
+ this.mPreciseCallState = state;
+ this.mNetworkType = networkType;
+ this.mCallQuality = callQuality;
+ }
+
+ @Override
+ public String toString() {
+ return "mPreciseCallState=" + mPreciseCallState + " mNetworkType=" + mNetworkType
+ + " mCallQuality=" + mCallQuality;
+ }
+
+ private CallAttributes(Parcel in) {
+ mPreciseCallState = (PreciseCallState) in.readValue(mPreciseCallState.getClass()
+ .getClassLoader());
+ mNetworkType = in.readInt();
+ mCallQuality = (CallQuality) in.readValue(mCallQuality.getClass().getClassLoader());
+ }
+
+ // getters
+ /**
+ * Returns the {@link PreciseCallState} of the call.
+ */
+ public PreciseCallState getPreciseCallState() {
+ return mPreciseCallState;
+ }
+
+ /**
+ * Returns the {@link TelephonyManager#NetworkType} of the call.
+ *
+ * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
+ * @see TelephonyManager#NETWORK_TYPE_GPRS
+ * @see TelephonyManager#NETWORK_TYPE_EDGE
+ * @see TelephonyManager#NETWORK_TYPE_UMTS
+ * @see TelephonyManager#NETWORK_TYPE_CDMA
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_0
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_A
+ * @see TelephonyManager#NETWORK_TYPE_1xRTT
+ * @see TelephonyManager#NETWORK_TYPE_HSDPA
+ * @see TelephonyManager#NETWORK_TYPE_HSUPA
+ * @see TelephonyManager#NETWORK_TYPE_HSPA
+ * @see TelephonyManager#NETWORK_TYPE_IDEN
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_B
+ * @see TelephonyManager#NETWORK_TYPE_LTE
+ * @see TelephonyManager#NETWORK_TYPE_EHRPD
+ * @see TelephonyManager#NETWORK_TYPE_HSPAP
+ * @see TelephonyManager#NETWORK_TYPE_GSM
+ * @see TelephonyManager#NETWORK_TYPE_TD_SCDMA
+ * @see TelephonyManager#NETWORK_TYPE_IWLAN
+ * @see TelephonyManager#NETWORK_TYPE_LTE_CA
+ * @see TelephonyManager#NETWORK_TYPE_NR
+ */
+ @NetworkType
+ public int getNetworkType() {
+ return mNetworkType;
+ }
+
+ /**
+ * Returns the {#link CallQuality} of the call.
+ */
+ public CallQuality getCallQuality() {
+ return mCallQuality;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPreciseCallState, mNetworkType, mCallQuality);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof CallAttributes) || hashCode() != o.hashCode()) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ CallAttributes s = (CallAttributes) o;
+
+ return (mPreciseCallState == s.mPreciseCallState
+ && mNetworkType == s.mNetworkType
+ && mCallQuality == s.mCallQuality);
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public @Parcelable.ContentsFlags int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+ mPreciseCallState.writeToParcel(dest, flags);
+ dest.writeInt(mNetworkType);
+ mCallQuality.writeToParcel(dest, flags);
+ }
+
+ public static final Parcelable.Creator<CallAttributes> CREATOR = new Parcelable.Creator() {
+ public CallAttributes createFromParcel(Parcel in) {
+ return new CallAttributes(in);
+ }
+
+ public CallAttributes[] newArray(int size) {
+ return new CallAttributes[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/CallQuality.aidl b/telephony/java/android/telephony/CallQuality.aidl
new file mode 100644
index 0000000..f54355f
--- /dev/null
+++ b/telephony/java/android/telephony/CallQuality.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable CallQuality;
+
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
new file mode 100644
index 0000000..b27f6b4
--- /dev/null
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Parcelable object to handle call quality.
+ * <p>
+ * Currently this supports IMS calls.
+ * <p>
+ * It provides the call quality level, duration, and additional information related to RTP packets,
+ * jitter and delay.
+ * <p>
+ * If there are multiple active calls, the CallQuality will pertain to the call in the foreground.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CallQuality implements Parcelable {
+
+ // Constants representing the call quality level (see #CallQuality);
+ public static final int CALL_QUALITY_EXCELLENT = 0;
+ public static final int CALL_QUALITY_GOOD = 1;
+ public static final int CALL_QUALITY_FAIR = 2;
+ public static final int CALL_QUALITY_POOR = 3;
+ public static final int CALL_QUALITY_BAD = 4;
+ public static final int CALL_QUALITY_NOT_AVAILABLE = 5;
+
+ /**
+ * Call quality
+ * @hide
+ */
+ @IntDef(prefix = { "CALL_QUALITY_" }, value = {
+ CALL_QUALITY_EXCELLENT,
+ CALL_QUALITY_GOOD,
+ CALL_QUALITY_FAIR,
+ CALL_QUALITY_POOR,
+ CALL_QUALITY_BAD,
+ CALL_QUALITY_NOT_AVAILABLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallQualityLevel {}
+
+ @CallQualityLevel
+ private int mDownlinkCallQualityLevel;
+ @CallQualityLevel
+ private int mUplinkCallQualityLevel;
+ private int mCallDuration;
+ private int mNumRtpPacketsTransmitted;
+ private int mNumRtpPacketsReceived;
+ private int mNumRtpPacketsTransmittedLost;
+ private int mNumRtpPacketsNotReceived;
+ private int mAverageRelativeJitter;
+ private int mMaxRelativeJitter;
+ private int mAverageRoundTripTime;
+ private int mCodecType;
+
+ /** @hide **/
+ public CallQuality(Parcel in) {
+ mDownlinkCallQualityLevel = in.readInt();
+ mUplinkCallQualityLevel = in.readInt();
+ mCallDuration = in.readInt();
+ mNumRtpPacketsTransmitted = in.readInt();
+ mNumRtpPacketsReceived = in.readInt();
+ mNumRtpPacketsTransmittedLost = in.readInt();
+ mNumRtpPacketsNotReceived = in.readInt();
+ mAverageRelativeJitter = in.readInt();
+ mMaxRelativeJitter = in.readInt();
+ mAverageRoundTripTime = in.readInt();
+ mCodecType = in.readInt();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param callQualityLevel the call quality level (see #CallQualityLevel)
+ * @param callDuration the call duration in milliseconds
+ * @param numRtpPacketsTransmitted RTP packets sent to network
+ * @param numRtpPacketsReceived RTP packets received from network
+ * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+ * transmitted
+ * @param numRtpPacketsNotReceived RTP packets which were lost in network and never recieved
+ * @param averageRelativeJitter average relative jitter in milliseconds
+ * @param maxRelativeJitter maximum relative jitter in milliseconds
+ * @param averageRoundTripTime average round trip delay in milliseconds
+ * @param codecType the codec type
+ */
+ public CallQuality(
+ @CallQualityLevel int downlinkCallQualityLevel,
+ @CallQualityLevel int uplinkCallQualityLevel,
+ int callDuration,
+ int numRtpPacketsTransmitted,
+ int numRtpPacketsReceived,
+ int numRtpPacketsTransmittedLost,
+ int numRtpPacketsNotReceived,
+ int averageRelativeJitter,
+ int maxRelativeJitter,
+ int averageRoundTripTime,
+ int codecType) {
+ this.mDownlinkCallQualityLevel = downlinkCallQualityLevel;
+ this.mUplinkCallQualityLevel = uplinkCallQualityLevel;
+ this.mCallDuration = callDuration;
+ this.mNumRtpPacketsTransmitted = numRtpPacketsTransmitted;
+ this.mNumRtpPacketsReceived = numRtpPacketsReceived;
+ this.mNumRtpPacketsTransmittedLost = numRtpPacketsTransmittedLost;
+ this.mNumRtpPacketsNotReceived = numRtpPacketsNotReceived;
+ this.mAverageRelativeJitter = averageRelativeJitter;
+ this.mMaxRelativeJitter = maxRelativeJitter;
+ this.mAverageRoundTripTime = averageRoundTripTime;
+ this.mCodecType = codecType;
+ }
+
+ // getters
+ /**
+ * Returns the downlink CallQualityLevel for a given ongoing call.
+ */
+ @CallQualityLevel
+ public int getDownlinkCallQualityLevel() {
+ return mDownlinkCallQualityLevel;
+ }
+
+ /**
+ * Returns the uplink CallQualityLevel for a given ongoing call.
+ */
+ @CallQualityLevel
+ public int getUplinkCallQualityLevel() {
+ return mUplinkCallQualityLevel;
+ }
+
+ /**
+ * Returns the duration of the call, in milliseconds.
+ */
+ public int getCallDuration() {
+ return mCallDuration;
+ }
+
+ /**
+ * Returns the total number of RTP packets transmitted by this device for a given ongoing call.
+ */
+ public int getNumRtpPacketsTransmitted() {
+ return mNumRtpPacketsTransmitted;
+ }
+
+ /**
+ * Returns the total number of RTP packets received by this device for a given ongoing call.
+ */
+ public int getNumRtpPacketsReceived() {
+ return mNumRtpPacketsReceived;
+ }
+
+ /**
+ * Returns the number of RTP packets which were sent by this device but were lost in the
+ * network before reaching the other party.
+ */
+ public int getNumRtpPacketsTransmittedLost() {
+ return mNumRtpPacketsTransmittedLost;
+ }
+
+ /**
+ * Returns the number of RTP packets which were sent by the other party but were lost in the
+ * network before reaching this device.
+ */
+ public int getNumRtpPacketsNotReceived() {
+ return mNumRtpPacketsNotReceived;
+ }
+
+ /**
+ * Returns the average relative jitter in milliseconds. Jitter represents the amount of variance
+ * in interarrival time of packets, for example, if two packets are sent 2 milliseconds apart
+ * but received 3 milliseconds apart, the relative jitter between those packets is 1
+ * millisecond.
+ *
+ * <p>See RFC 3550 for more information on jitter calculations.
+ */
+ public int getAverageRelativeJitter() {
+ return mAverageRelativeJitter;
+ }
+
+ /**
+ * Returns the maximum relative jitter for a given ongoing call. Jitter represents the amount of
+ * variance in interarrival time of packets, for example, if two packets are sent 2 milliseconds
+ * apart but received 3 milliseconds apart, the relative jitter between those packets is 1
+ * millisecond.
+ *
+ * <p>See RFC 3550 for more information on jitter calculations.
+ */
+ public int getMaxRelativeJitter() {
+ return mMaxRelativeJitter;
+ }
+
+ /**
+ * Returns the average round trip time in milliseconds.
+ */
+ public int getAverageRoundTripTime() {
+ return mAverageRoundTripTime;
+ }
+
+ /**
+ * Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in
+ * {@link ImsStreamMediaProfile}.
+ *
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_NONE
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR_WB
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_QCELP13K
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_B
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_WB
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_NW
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_EFR
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_FR
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_HR
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711U
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_G723
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711A
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_G722
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711AB
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_G729
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_NB
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_WB
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_SWB
+ * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_FB
+ */
+ public int getCodecType() {
+ return mCodecType;
+ }
+
+ // Parcelable things
+ @Override
+ public String toString() {
+ return "CallQuality: {downlinkCallQualityLevel=" + mDownlinkCallQualityLevel
+ + " uplinkCallQualityLevel=" + mUplinkCallQualityLevel
+ + " callDuration=" + mCallDuration
+ + " numRtpPacketsTransmitted=" + mNumRtpPacketsTransmitted
+ + " numRtpPacketsReceived=" + mNumRtpPacketsReceived
+ + " numRtpPacketsTransmittedLost=" + mNumRtpPacketsTransmittedLost
+ + " numRtpPacketsNotReceived=" + mNumRtpPacketsNotReceived
+ + " averageRelativeJitter=" + mAverageRelativeJitter
+ + " maxRelativeJitter=" + mMaxRelativeJitter
+ + " averageRoundTripTime=" + mAverageRoundTripTime
+ + " codecType=" + mCodecType
+ + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mDownlinkCallQualityLevel,
+ mUplinkCallQualityLevel,
+ mCallDuration,
+ mNumRtpPacketsTransmitted,
+ mNumRtpPacketsReceived,
+ mNumRtpPacketsTransmittedLost,
+ mNumRtpPacketsNotReceived,
+ mAverageRelativeJitter,
+ mMaxRelativeJitter,
+ mAverageRoundTripTime,
+ mCodecType);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof CallQuality) || hashCode() != o.hashCode()) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ CallQuality s = (CallQuality) o;
+
+ return (mDownlinkCallQualityLevel == s.mDownlinkCallQualityLevel
+ && mUplinkCallQualityLevel == s.mUplinkCallQualityLevel
+ && mCallDuration == s.mCallDuration
+ && mNumRtpPacketsTransmitted == s.mNumRtpPacketsTransmitted
+ && mNumRtpPacketsReceived == s.mNumRtpPacketsReceived
+ && mNumRtpPacketsTransmittedLost == s.mNumRtpPacketsTransmittedLost
+ && mNumRtpPacketsNotReceived == s.mNumRtpPacketsNotReceived
+ && mAverageRelativeJitter == s.mAverageRelativeJitter
+ && mMaxRelativeJitter == s.mMaxRelativeJitter
+ && mAverageRoundTripTime == s.mAverageRoundTripTime
+ && mCodecType == s.mCodecType);
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public @Parcelable.ContentsFlags int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+ dest.writeInt(mDownlinkCallQualityLevel);
+ dest.writeInt(mUplinkCallQualityLevel);
+ dest.writeInt(mCallDuration);
+ dest.writeInt(mNumRtpPacketsTransmitted);
+ dest.writeInt(mNumRtpPacketsReceived);
+ dest.writeInt(mNumRtpPacketsTransmittedLost);
+ dest.writeInt(mNumRtpPacketsNotReceived);
+ dest.writeInt(mAverageRelativeJitter);
+ dest.writeInt(mMaxRelativeJitter);
+ dest.writeInt(mAverageRoundTripTime);
+ dest.writeInt(mCodecType);
+ }
+
+ public static final Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() {
+ public CallQuality createFromParcel(Parcel in) {
+ return new CallQuality(in);
+ }
+
+ public CallQuality[] newArray(int size) {
+ return new CallQuality[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/ICellInfoCallback.aidl b/telephony/java/android/telephony/ICellInfoCallback.aidl
index 7fb62682..ee3c1b1 100644
--- a/telephony/java/android/telephony/ICellInfoCallback.aidl
+++ b/telephony/java/android/telephony/ICellInfoCallback.aidl
@@ -16,6 +16,7 @@
package android.telephony;
+import android.os.ParcelableException;
import android.telephony.CellInfo;
import java.util.List;
@@ -27,4 +28,5 @@
oneway interface ICellInfoCallback
{
void onCellInfo(in List<CellInfo> state);
+ void onError(in int errorCode, in ParcelableException detail);
}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index e27b385..c816701 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -296,7 +296,7 @@
/**
* Listen for changes to preferred data subId.
- * See {@link SubscriptionManager#setPreferredData(int)}
+ * See {@link SubscriptionManager#setPreferredDataSubId(int)}
* for more details.
*
* @see #onPreferredDataSubIdChanged
@@ -335,6 +335,18 @@
@SystemApi
public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000;
+ /**
+ * Listen for changes to the call attributes of a currently active call.
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE}
+ *
+ * @see #onCallAttributesChanged
+ * @hide
+ */
+ @SystemApi
+ public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 0x04000000;
+
/*
* Subscription used to listen to the phone state changes
* @hide
@@ -683,6 +695,17 @@
}
/**
+ * Callback invoked when the call attributes changes. Requires
+ * the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param callAttributes the call attributes
+ * @hide
+ */
+ @SystemApi
+ public void onCallAttributesChanged(CallAttributes callAttributes) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when modem radio power state changes. Requires
* the READ_PRIVILEGED_PHONE_STATE permission.
* @param state the modem radio power state
@@ -941,6 +964,14 @@
() -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state)));
}
+ public void onCallAttributesChanged(CallAttributes callAttributes) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes)));
+ }
+
public void onPreferredDataSubIdChanged(int subId) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 2271069..e77042d 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -24,6 +24,8 @@
import android.os.Parcelable;
import android.os.PersistableBundle;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -178,6 +180,35 @@
return mLte;
}
+ /**
+ * Returns a List of CellSignalStrength Components of this SignalStrength Report.
+ *
+ * Use this API to access underlying
+ * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+ * granular information about the SignalStrength report. Only valid (non-empty)
+ * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
+ * and the list may contain more than one instance of a CellSignalStrength type.
+ *
+ * @return a List of CellSignalStrength or an empty List if there are no valid measurements.
+ *
+ * @see android.telephony#CellSignalStrength
+ * @see android.telephony#CellSignalStrengthNr
+ * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony#CellSignalStrengthGsm
+ */
+ public @NonNull List<CellSignalStrength> getCellSignalStrengths() {
+ List<CellSignalStrength> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems
+ if (mLte.isValid()) cssList.add(mLte);
+ if (mCdma.isValid()) cssList.add(mCdma);
+ if (mTdscdma.isValid()) cssList.add(mTdscdma);
+ if (mWcdma.isValid()) cssList.add(mWcdma);
+ if (mGsm.isValid()) cssList.add(mGsm);
+ return cssList;
+ }
+
/** @hide */
public void updateLevel(PersistableBundle cc, ServiceState ss) {
mLteRsrpBoost = ss.getLteEarfcnRsrpBoost();
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 34f7abd..9fa4c3c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2453,10 +2453,39 @@
*
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void setPreferredData(int subId) {
- if (VDBG) logd("[setPreferredData]+ subId:" + subId);
- setSubscriptionPropertyHelper(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
- "setPreferredData", (iSub)-> iSub.setPreferredData(subId));
+ public void setPreferredDataSubscriptionId(int subId) {
+ if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId);
+ setSubscriptionPropertyHelper(DEFAULT_SUBSCRIPTION_ID, "setPreferredDataSubscriptionId",
+ (iSub)-> iSub.setPreferredDataSubscriptionId(subId));
+ }
+
+ /**
+ * Get which subscription is preferred for cellular data.
+ * It's also usually the subscription we set up internet connection on.
+ *
+ * PreferredData overwrites user setting of default data subscription. And it's used
+ * by AlternativeNetworkService or carrier apps to switch primary and CBRS
+ * subscription dynamically in multi-SIM devices.
+ *
+ * @return preferred subscription id for cellular data. {@link DEFAULT_SUBSCRIPTION_ID} if
+ * there's no prefered subscription.
+ *
+ * @hide
+ *
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getPreferredDataSubscriptionId() {
+ int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ preferredSubId = iSub.getPreferredDataSubscriptionId();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return preferredSubId;
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b48a1ce..babeb7b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4898,19 +4898,53 @@
/** Callback for providing asynchronous {@link CellInfo} on request */
public abstract static class CellInfoCallback {
/**
- * Response to
+ * Success response to
* {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
*
- * <p>Invoked when there is a response to
+ * Invoked when there is a response to
* {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}
* to provide a list of {@link CellInfo}. If no {@link CellInfo} is available then an empty
- * list will be provided. If an error occurs, null will be provided.
+ * list will be provided. If an error occurs, null will be provided unless the onError
+ * callback is overridden.
*
* @param cellInfo a list of {@link CellInfo}, an empty list, or null.
*
* {@see android.telephony.TelephonyManager#getAllCellInfo getAllCellInfo()}
*/
- public abstract void onCellInfo(List<CellInfo> cellInfo);
+ public abstract void onCellInfo(@NonNull List<CellInfo> cellInfo);
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ERROR_"}, value = {ERROR_TIMEOUT, ERROR_MODEM_ERROR})
+ public @interface CellInfoCallbackError {}
+
+ /**
+ * The system timed out waiting for a response from the Radio.
+ */
+ public static final int ERROR_TIMEOUT = 1;
+
+ /**
+ * The modem returned a failure.
+ */
+ public static final int ERROR_MODEM_ERROR = 2;
+
+ /**
+ * Error response to
+ * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
+ *
+ * Invoked when an error condition prevents updated {@link CellInfo} from being fetched
+ * and returned from the modem. Callers of requestCellInfoUpdate() should override this
+ * function to receive detailed status information in the event of an error. By default,
+ * this function will invoke onCellInfo() with null.
+ *
+ * @param errorCode an error code indicating the type of failure.
+ * @param detail a Throwable object with additional detail regarding the failure if
+ * available, otherwise null.
+ */
+ public void onError(@CellInfoCallbackError int errorCode, @Nullable Throwable detail) {
+ // By default, simply invoke the success callback with an empty list.
+ onCellInfo(new ArrayList<CellInfo>());
+ }
};
/**
@@ -4937,6 +4971,12 @@
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> callback.onCellInfo(cellInfo)));
}
+
+ public void onError(int errorCode, android.os.ParcelableException detail) {
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() -> callback.onError(
+ errorCode, detail.getCause())));
+ }
}, getOpPackageName());
} catch (RemoteException ex) {
@@ -4971,6 +5011,12 @@
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> callback.onCellInfo(cellInfo)));
}
+
+ public void onError(int errorCode, android.os.ParcelableException detail) {
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() -> callback.onError(
+ errorCode, detail.getCause())));
+ }
}, getOpPackageName(), workSource);
} catch (RemoteException ex) {
}
@@ -9864,10 +9910,11 @@
try {
IOns iOpportunisticNetworkService = getIOns();
if (iOpportunisticNetworkService != null) {
- return iOpportunisticNetworkService.setPreferredData(subId, pkgForDebug);
+ return iOpportunisticNetworkService
+ .setPreferredDataSubscriptionId(subId, pkgForDebug);
}
} catch (RemoteException ex) {
- Rlog.e(TAG, "setPreferredData RemoteException", ex);
+ Rlog.e(TAG, "setPreferredDataSubscriptionId RemoteException", ex);
}
return false;
}
@@ -9888,10 +9935,10 @@
try {
IOns iOpportunisticNetworkService = getIOns();
if (iOpportunisticNetworkService != null) {
- subId = iOpportunisticNetworkService.getPreferredData(pkgForDebug);
+ subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId(pkgForDebug);
}
} catch (RemoteException ex) {
- Rlog.e(TAG, "getPreferredData RemoteException", ex);
+ Rlog.e(TAG, "getPreferredDataSubscriptionId RemoteException", ex);
}
return subId;
}
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index df903cc2..397d5d9 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -443,6 +443,13 @@
public void callSessionRttMessageReceived(String rttMessage) {
// no-op
}
+
+ /**
+ * While in call, there has been a change in RTT audio indicator.
+ */
+ public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+ // no-op
+ }
}
private final IImsCallSession miSession;
@@ -1397,6 +1404,16 @@
mListener.callSessionRttMessageReceived(rttMessage);
}
}
+
+ /**
+ * While in call, there has been a change in RTT audio indicator.
+ */
+ @Override
+ public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionRttAudioIndicatorChanged(profile);
+ }
+ }
}
/**
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index a7f124a..a4696a3 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -599,5 +599,18 @@
throw new RuntimeException(e);
}
}
+
+ /**
+ * While in call, there has been a change in RTT audio indicator.
+ *
+ * @param profile updated ImsStreamMediaProfile
+ */
+ public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+ try {
+ mListener.callSessionRttAudioIndicatorChanged(profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index 52d72b5..837ef54 100644
--- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
@@ -97,6 +97,9 @@
// Rtt related information
/** @hide */
public int mRttMode;
+ // RTT Audio Speech Indicator
+ /** @hide */
+ public boolean mHasRttAudioSpeech = false;
/** @hide */
public ImsStreamMediaProfile(Parcel in) {
@@ -197,7 +200,8 @@
", audioDirection=" + mAudioDirection +
", videoQuality=" + mVideoQuality +
", videoDirection=" + mVideoDirection +
- ", rttMode=" + mRttMode + " }";
+ ", rttMode=" + mRttMode +
+ ", hasRttAudioSpeech=" + mHasRttAudioSpeech + " }";
}
@Override
@@ -212,6 +216,7 @@
out.writeInt(mVideoQuality);
out.writeInt(mVideoDirection);
out.writeInt(mRttMode);
+ out.writeBoolean(mHasRttAudioSpeech);
}
private void readFromParcel(Parcel in) {
@@ -220,6 +225,7 @@
mVideoQuality = in.readInt();
mVideoDirection = in.readInt();
mRttMode = in.readInt();
+ mHasRttAudioSpeech = in.readBoolean();
}
public static final Creator<ImsStreamMediaProfile> CREATOR =
@@ -250,6 +256,10 @@
mRttMode = rttMode;
}
+ public void setRttAudioSpeech(boolean audioOn) {
+ mHasRttAudioSpeech = audioOn;
+ }
+
public int getAudioQuality() {
return mAudioQuality;
}
@@ -269,4 +279,8 @@
public int getRttMode() {
return mRttMode;
}
+
+ public boolean getRttAudioSpeech() {
+ return mHasRttAudioSpeech;
+ }
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index f25b4b1..d0b31e1 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -138,4 +138,10 @@
* @param rttMessage Received RTT message
*/
void callSessionRttMessageReceived(in String rttMessage);
+
+ /*
+ * While in call, there has been a change in RTT audio indicator.
+ * @param profile updated ImsStreamMediaProfile
+ */
+ void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
}
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index 23de2fd..bc58e46 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -591,5 +591,11 @@
public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
mNewListener.callSessionRttMessageReceived(rttMessage);
}
+
+ @Override
+ public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile)
+ throws RemoteException {
+ mNewListener.callSessionRttAudioIndicatorChanged(profile);
+ }
}
}
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index a8e8b7dd..bbb27af 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -152,4 +152,10 @@
* @param rttMessage Received RTT message
*/
void callSessionRttMessageReceived(in String rttMessage);
+
+ /*
+ * While in call, there has been a change in RTT audio indicator.
+ * @param profile updated ImsStreamMediaProfile
+ */
+ void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
}
diff --git a/telephony/java/com/android/internal/telephony/IOns.aidl b/telephony/java/com/android/internal/telephony/IOns.aidl
index d6779f1..0e3d12b 100755
--- a/telephony/java/com/android/internal/telephony/IOns.aidl
+++ b/telephony/java/com/android/internal/telephony/IOns.aidl
@@ -66,7 +66,7 @@
* @return true if request is accepted, else false.
*
*/
- boolean setPreferredData(int subId, String callingPackage);
+ boolean setPreferredDataSubscriptionId(int subId, String callingPackage);
/**
* Get preferred opportunistic data subscription Id
@@ -78,7 +78,7 @@
* subscription id
*
*/
- int getPreferredData(String callingPackage);
+ int getPreferredDataSubscriptionId(String callingPackage);
/**
* Update availability of a list of networks in the current location.
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 00cf9c3..3dbebe8 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -17,14 +17,15 @@
package com.android.internal.telephony;
import android.os.Bundle;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
+import android.telephony.CallAttributes;
import android.telephony.CellInfo;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.PhoneCapability;
import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
oneway interface IPhoneStateListener {
@@ -54,6 +55,7 @@
void onPhoneCapabilityChanged(in PhoneCapability capability);
void onPreferredDataSubIdChanged(in int subId);
void onRadioPowerStateChanged(in int state);
+ void onCallAttributesChanged(in CallAttributes callAttributes);
void onEmergencyNumberListChanged(in Map emergencyNumberList);
void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index d169b7d..577ddbd 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -200,7 +200,15 @@
* @hide
*
*/
- int setPreferredData(int subId);
+ int setPreferredDataSubscriptionId(int subId);
+
+ /**
+ * Get which subscription is preferred for cellular data.
+ *
+ * @hide
+ *
+ */
+ int getPreferredDataSubscriptionId();
/**
* Get User downloaded Profiles.
diff --git a/tests/PackageWatchdog/Android.mk b/tests/PackageWatchdog/Android.mk
index b53f27d..1c1c2a4 100644
--- a/tests/PackageWatchdog/Android.mk
+++ b/tests/PackageWatchdog/Android.mk
@@ -23,7 +23,7 @@
junit \
frameworks-base-testutils \
android-support-test \
- services
+ services.core
LOCAL_JAVA_LIBRARIES := \
android.test.runner
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index f82b380..932fee0 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -33,6 +34,9 @@
import android.system.OsConstants;
import android.util.ArraySet;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
@@ -41,9 +45,6 @@
import java.util.List;
import java.util.Set;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class LinkPropertiesTest {
@@ -504,6 +505,40 @@
}
@Test
+ public void testNat64Prefix() throws Exception {
+ LinkProperties lp = new LinkProperties();
+ lp.addLinkAddress(LINKADDRV4);
+ lp.addLinkAddress(LINKADDRV6);
+
+ assertNull(lp.getNat64Prefix());
+
+ IpPrefix p = new IpPrefix("64:ff9b::/96");
+ lp.setNat64Prefix(p);
+ assertEquals(p, lp.getNat64Prefix());
+
+ p = new IpPrefix("2001:db8:a:b:1:2:3::/96");
+ lp.setNat64Prefix(p);
+ assertEquals(p, lp.getNat64Prefix());
+
+ p = new IpPrefix("2001:db8:a:b:1:2::/80");
+ try {
+ lp.setNat64Prefix(p);
+ } catch (IllegalArgumentException expected) {
+ }
+
+ p = new IpPrefix("64:ff9b::/64");
+ try {
+ lp.setNat64Prefix(p);
+ } catch (IllegalArgumentException expected) {
+ }
+
+ assertEquals(new IpPrefix("2001:db8:a:b:1:2:3::/96"), lp.getNat64Prefix());
+
+ lp.setNat64Prefix(null);
+ assertNull(lp.getNat64Prefix());
+ }
+
+ @Test
public void testIsProvisioned() {
LinkProperties lp4 = new LinkProperties();
assertFalse("v4only:empty", lp4.isProvisioned());
@@ -815,7 +850,7 @@
}
@Test
- public void testLinkPropertiesParcelable() {
+ public void testLinkPropertiesParcelable() throws Exception {
LinkProperties source = new LinkProperties();
source.setInterfaceName(NAME);
// set 2 link addresses
@@ -833,6 +868,8 @@
source.setMtu(MTU);
+ source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
+
Parcel p = Parcel.obtain();
source.writeToParcel(p, /* flags */ 0);
p.setDataPosition(0);
diff --git a/tools/signedconfig/prod_public.pem b/tools/signedconfig/prod_public.pem
new file mode 100644
index 0000000..8c10215
--- /dev/null
+++ b/tools/signedconfig/prod_public.pem
@@ -0,0 +1,5 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+lky6wKyGL6lE1VrD0YTMHwb0Xwc
++tzC8MvnrzVxodvTpVY/jV7V+Zktcx+pry43XPABFRXtbhTo+qykhyBA1g==
+-----END PUBLIC KEY-----
+
diff --git a/tools/signedconfig/verify_b64.sh b/tools/signedconfig/verify_b64.sh
index 8e1f58c..a4ac6a8 100755
--- a/tools/signedconfig/verify_b64.sh
+++ b/tools/signedconfig/verify_b64.sh
@@ -7,4 +7,30 @@
# The arg values can be taken from the debug log for SignedConfigService when verbose logging is
# enabled.
-openssl dgst -sha256 -verify $(dirname $0)/debug_public.pem -signature <(echo $2 | base64 -d) <(echo $1 | base64 -d)
+function verify() {
+ D=${1}
+ S=${2}
+ K=${3}
+ echo Trying ${K}
+ openssl dgst -sha256 -verify $(dirname $0)/${K} -signature <(echo ${S} | base64 -d) <(echo ${D} | base64 -d)
+}
+
+
+PROD_KEY_NAME=prod_public.pem
+DEBUG_KEY_NAME=debug_public.pem
+SIGNATURE="$2"
+DATA="$1"
+
+echo DATA: ${DATA}
+echo SIGNATURE: ${SIGNATURE}
+
+if verify "${DATA}" "${SIGNATURE}" "${PROD_KEY_NAME}"; then
+ echo Verified with ${PROD_KEY_NAME}
+ exit 0
+fi
+
+if verify "${DATA}" "${SIGNATURE}" "${DEBUG_KEY_NAME}"; then
+ echo Verified with ${DEBUG_KEY_NAME}
+ exit 0
+fi
+exit 1
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 7689fc3..59a7290 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -572,7 +572,7 @@
@Override
public int hashCode() {
- return Objects.hash(mCertType, mCertSha256Fingerprint);
+ return Objects.hash(mCertType, Arrays.hashCode(mCertSha256Fingerprint));
}
@Override
@@ -842,24 +842,50 @@
}
/**
- * CA (Certificate Authority) X509 certificate.
+ * CA (Certificate Authority) X509 certificates.
*/
- private X509Certificate mCaCertificate = null;
+ private X509Certificate[] mCaCertificates = null;
+
/**
* Set the CA (Certification Authority) certificate associated with this credential.
*
* @param caCertificate The CA certificate to set to
*/
public void setCaCertificate(X509Certificate caCertificate) {
- mCaCertificate = caCertificate;
+ mCaCertificates = null;
+ if (caCertificate != null) {
+ mCaCertificates = new X509Certificate[] {caCertificate};
+ }
}
+
+ /**
+ * Set the CA (Certification Authority) certificates associated with this credential.
+ *
+ * @param caCertificates The list of CA certificates to set to
+ * @hide
+ */
+ public void setCaCertificates(X509Certificate[] caCertificates) {
+ mCaCertificates = caCertificates;
+ }
+
/**
* Get the CA (Certification Authority) certificate associated with this credential.
*
- * @return CA certificate associated with this credential
+ * @return CA certificate associated with this credential, {@code null} if certificate is not
+ * set or certificate is more than one.
*/
public X509Certificate getCaCertificate() {
- return mCaCertificate;
+ return mCaCertificates == null || mCaCertificates.length > 1 ? null : mCaCertificates[0];
+ }
+
+ /**
+ * Get the CA (Certification Authority) certificates associated with this credential.
+ *
+ * @return The list of CA certificates associated with this credential
+ * @hide
+ */
+ public X509Certificate[] getCaCertificates() {
+ return mCaCertificates;
}
/**
@@ -933,7 +959,11 @@
mClientCertificateChain = Arrays.copyOf(source.mClientCertificateChain,
source.mClientCertificateChain.length);
}
- mCaCertificate = source.mCaCertificate;
+ if (source.mCaCertificates != null) {
+ mCaCertificates = Arrays.copyOf(source.mCaCertificates,
+ source.mCaCertificates.length);
+ }
+
mClientPrivateKey = source.mClientPrivateKey;
}
}
@@ -952,7 +982,7 @@
dest.writeParcelable(mUserCredential, flags);
dest.writeParcelable(mCertCredential, flags);
dest.writeParcelable(mSimCredential, flags);
- ParcelUtil.writeCertificate(dest, mCaCertificate);
+ ParcelUtil.writeCertificates(dest, mCaCertificates);
ParcelUtil.writeCertificates(dest, mClientCertificateChain);
ParcelUtil.writePrivateKey(dest, mClientPrivateKey);
}
@@ -977,16 +1007,17 @@
: mCertCredential.equals(that.mCertCredential))
&& (mSimCredential == null ? that.mSimCredential == null
: mSimCredential.equals(that.mSimCredential))
- && isX509CertificateEquals(mCaCertificate, that.mCaCertificate)
+ && isX509CertificatesEquals(mCaCertificates, that.mCaCertificates)
&& isX509CertificatesEquals(mClientCertificateChain, that.mClientCertificateChain)
&& isPrivateKeyEquals(mClientPrivateKey, that.mClientPrivateKey);
}
@Override
public int hashCode() {
- return Objects.hash(mRealm, mCreationTimeInMillis, mExpirationTimeInMillis,
+ return Objects.hash(mCreationTimeInMillis, mExpirationTimeInMillis, mRealm,
mCheckAaaServerCertStatus, mUserCredential, mCertCredential, mSimCredential,
- mCaCertificate, mClientCertificateChain, mClientPrivateKey);
+ mClientPrivateKey, Arrays.hashCode(mCaCertificates),
+ Arrays.hashCode(mClientCertificateChain));
}
@Override
@@ -1067,7 +1098,7 @@
credential.setUserCredential(in.readParcelable(null));
credential.setCertCredential(in.readParcelable(null));
credential.setSimCredential(in.readParcelable(null));
- credential.setCaCertificate(ParcelUtil.readCertificate(in));
+ credential.setCaCertificates(ParcelUtil.readCertificates(in));
credential.setClientCertificateChain(ParcelUtil.readCertificates(in));
credential.setClientPrivateKey(ParcelUtil.readPrivateKey(in));
return credential;
@@ -1100,7 +1131,7 @@
// CA certificate is required for R1 Passpoint profile.
// For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
- if (isR1 && mCaCertificate == null) {
+ if (isR1 && mCaCertificates == null) {
Log.d(TAG, "Missing CA Certificate for user credential");
return false;
}
@@ -1131,7 +1162,7 @@
// Verify required key and certificates for certificate credential.
// CA certificate is required for R1 Passpoint profile.
// For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
- if (isR1 && mCaCertificate == null) {
+ if (isR1 && mCaCertificates == null) {
Log.d(TAG, "Missing CA Certificate for certificate credential");
return false;
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index a9d4b8f..1ecc3fe 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -16,6 +16,7 @@
package android.net.wifi.hotspot2.pps;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -44,17 +45,16 @@
* @param userCred Instance of UserCredential
* @param certCred Instance of CertificateCredential
* @param simCred Instance of SimCredential
- * @param caCert CA certificate
* @param clientCertificateChain Chain of client certificates
* @param clientPrivateKey Client private key
+ * @param caCerts CA certificates
* @return {@link Credential}
*/
private static Credential createCredential(Credential.UserCredential userCred,
- Credential.CertificateCredential certCred,
- Credential.SimCredential simCred,
- X509Certificate caCert,
- X509Certificate[] clientCertificateChain,
- PrivateKey clientPrivateKey) {
+ Credential.CertificateCredential certCred,
+ Credential.SimCredential simCred,
+ X509Certificate[] clientCertificateChain, PrivateKey clientPrivateKey,
+ X509Certificate... caCerts) {
Credential cred = new Credential();
cred.setCreationTimeInMillis(123455L);
cred.setExpirationTimeInMillis(2310093L);
@@ -63,7 +63,11 @@
cred.setUserCredential(userCred);
cred.setCertCredential(certCred);
cred.setSimCredential(simCred);
- cred.setCaCertificate(caCert);
+ if (caCerts != null && caCerts.length == 1) {
+ cred.setCaCertificate(caCerts[0]);
+ } else {
+ cred.setCaCertificates(caCerts);
+ }
cred.setClientCertificateChain(clientCertificateChain);
cred.setClientPrivateKey(clientPrivateKey);
return cred;
@@ -80,8 +84,8 @@
certCred.setCertType("x509v3");
certCred.setCertSha256Fingerprint(
MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
- return createCredential(null, certCred, null, FakeKeys.CA_CERT0,
- new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
+ return createCredential(null, certCred, null, new X509Certificate[] {FakeKeys.CLIENT_CERT},
+ FakeKeys.RSA_KEY1, FakeKeys.CA_CERT0, FakeKeys.CA_CERT1);
}
/**
@@ -93,7 +97,7 @@
Credential.SimCredential simCred = new Credential.SimCredential();
simCred.setImsi("1234*");
simCred.setEapType(EAPConstants.EAP_SIM);
- return createCredential(null, null, simCred, null, null, null);
+ return createCredential(null, null, simCred, null, null, (X509Certificate[]) null);
}
/**
@@ -110,7 +114,7 @@
userCred.setSoftTokenApp("TestApp");
userCred.setEapType(EAPConstants.EAP_TTLS);
userCred.setNonEapInnerMethod("MS-CHAP");
- return createCredential(userCred, null, null, FakeKeys.CA_CERT0, null, null);
+ return createCredential(userCred, null, null, null, null, FakeKeys.CA_CERT0);
}
private static void verifyParcel(Credential writeCred) {
@@ -120,6 +124,7 @@
parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
Credential readCred = Credential.CREATOR.createFromParcel(parcel);
assertTrue(readCred.equals(writeCred));
+ assertEquals(writeCred.hashCode(), readCred.hashCode());
}
/**